From e5fbd94650d987e273e5dbd94d4d9727d5ea8df1 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Sun, 22 Dec 2024 23:56:15 +0100 Subject: [PATCH 01/57] Bump to 8.0.0 in preparation. --- NEWS | 1 + VERSION | 2 +- configure | 22 +++++++++++----------- include/pqxx/doc/mainpage.md | 2 +- include/pqxx/version.hxx | 10 +++++----- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/NEWS b/NEWS index b61940d63..fe335ae49 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,4 @@ +8.0.0 7.10.1 - Fix string conversion buffer budget for arrays containing nulls. (#921) - Remove `-fanalyzer` option again; gcc is still broken. diff --git a/VERSION b/VERSION index d6e2f7b0a..ae9a76b92 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.10.1 +8.0.0 diff --git a/configure b/configure index 866092d59..a279c9091 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for libpqxx 7.10.1. +# Generated by GNU Autoconf 2.71 for libpqxx 8.0.0. # # Report bugs to . # @@ -621,8 +621,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='libpqxx' PACKAGE_TARNAME='libpqxx' -PACKAGE_VERSION='7.10.1' -PACKAGE_STRING='libpqxx 7.10.1' +PACKAGE_VERSION='8.0.0' +PACKAGE_STRING='libpqxx 8.0.0' PACKAGE_BUGREPORT='Jeroen T. Vermeulen' PACKAGE_URL='' @@ -1381,7 +1381,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libpqxx 7.10.1 to adapt to many kinds of systems. +\`configure' configures libpqxx 8.0.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1452,7 +1452,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libpqxx 7.10.1:";; + short | recursive ) echo "Configuration of libpqxx 8.0.0:";; esac cat <<\_ACEOF @@ -1576,7 +1576,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libpqxx configure 7.10.1 +libpqxx configure 8.0.0 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -1951,7 +1951,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libpqxx $as_me 7.10.1, which was +It was created by libpqxx $as_me 8.0.0, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3446,7 +3446,7 @@ fi # Define the identity of the package. PACKAGE='libpqxx' - VERSION='7.10.1' + VERSION='8.0.0' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -3550,7 +3550,7 @@ END fi -PQXX_ABI=7.10 +PQXX_ABI=8.0 PQXXVERSION=$PACKAGE_VERSION @@ -19328,7 +19328,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libpqxx $as_me 7.10.1, which was +This file was extended by libpqxx $as_me 8.0.0, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -19396,7 +19396,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -libpqxx config.status 7.10.1 +libpqxx config.status 8.0.0 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/include/pqxx/doc/mainpage.md b/include/pqxx/doc/mainpage.md index 855304b59..2ce0675f8 100644 --- a/include/pqxx/doc/mainpage.md +++ b/include/pqxx/doc/mainpage.md @@ -1,7 +1,7 @@ libpqxx {#mainpage} ======= -@version 7.10.1 +@version 8.0.0 @author Jeroen T. Vermeulen @see https://pqxx.org/libpqxx/ @see https://github.com/jtv/libpqxx diff --git a/include/pqxx/version.hxx b/include/pqxx/version.hxx index d0bf552c1..3d4f57969 100644 --- a/include/pqxx/version.hxx +++ b/include/pqxx/version.hxx @@ -16,16 +16,16 @@ # endif /// Full libpqxx version string. -# define PQXX_VERSION "7.10.1" +# define PQXX_VERSION "8.0.0" /// Library ABI version. -# define PQXX_ABI "7.10" +# define PQXX_ABI "8.0" /// Major version number. -# define PQXX_VERSION_MAJOR 7 +# define PQXX_VERSION_MAJOR 8 /// Minor version number. -# define PQXX_VERSION_MINOR 10 +# define PQXX_VERSION_MINOR 0 -# define PQXX_VERSION_CHECK check_pqxx_version_7_10 +# define PQXX_VERSION_CHECK check_pqxx_version_8_0 namespace pqxx::internal { From d275085f1907424d97321f962ebd19d8137ccfd7 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 01:06:13 +0100 Subject: [PATCH 02/57] Retire check for integral `` conversions. --- NEWS | 2 + cmake/pqxx_cxx_feature_checks.cmake | 4 - config-tests/PQXX_HAVE_CHARCONV_INT.cxx | 16 --- configitems | 1 - configure | 50 ------- include/pqxx/config.h.in | 3 - pqxx_cxx_feature_checks.ac | 10 -- src/strconv.cxx | 173 +----------------------- 8 files changed, 4 insertions(+), 255 deletions(-) delete mode 100644 config-tests/PQXX_HAVE_CHARCONV_INT.cxx diff --git a/NEWS b/NEWS index fe335ae49..8d5c4062e 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,6 @@ 8.0.0 + - C++20 is now the oldest C++ version that libpqxx supports. + - Compiler must support integral conversions in `charconv`. 7.10.1 - Fix string conversion buffer budget for arrays containing nulls. (#921) - Remove `-fanalyzer` option again; gcc is still broken. diff --git a/cmake/pqxx_cxx_feature_checks.cmake b/cmake/pqxx_cxx_feature_checks.cmake index ac51729d3..4b498620a 100644 --- a/cmake/pqxx_cxx_feature_checks.cmake +++ b/cmake/pqxx_cxx_feature_checks.cmake @@ -7,10 +7,6 @@ try_compile( PQXX_HAVE_CHARCONV_FLOAT ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CHARCONV_FLOAT.cxx ) -try_compile( - PQXX_HAVE_CHARCONV_INT ${PROJECT_BINARY_DIR} - SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CHARCONV_INT.cxx -) try_compile( PQXX_HAVE_CMP ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CMP.cxx diff --git a/config-tests/PQXX_HAVE_CHARCONV_INT.cxx b/config-tests/PQXX_HAVE_CHARCONV_INT.cxx deleted file mode 100644 index 076ee0de3..000000000 --- a/config-tests/PQXX_HAVE_CHARCONV_INT.cxx +++ /dev/null @@ -1,16 +0,0 @@ -// Test for std::to_string/std::from_string for integral types. -#include -#include - -int main() -{ - char z[100]; - auto rt = std::to_chars(std::begin(z), std::end(z), 9ULL); - if (rt.ec != std::errc{}) - return 1; - unsigned long long n; - auto rf = std::from_chars(std::cbegin(z), std::cend(z), n); - if (rf.ec != std::errc{}) - return 2; - return (n == 9ULL) ? 0 : 1; -} diff --git a/configitems b/configitems index da97c35db..757e9015d 100644 --- a/configitems +++ b/configitems @@ -5,7 +5,6 @@ PACKAGE_STRING internal autotools PACKAGE_TARNAME internal autotools PACKAGE_VERSION internal autotools PQXX_HAVE_ASSUME public compiler -PQXX_HAVE_CHARCONV_INT internal compiler PQXX_HAVE_CHARCONV_FLOAT internal compiler PQXX_HAVE_CMP public compiler PQXX_HAVE_CONCEPTS public compiler diff --git a/configure b/configure index a279c9091..2a48b885d 100755 --- a/configure +++ b/configure @@ -17324,56 +17324,6 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_CHARCONV_FLOAT" >&5 printf "%s\n" "$PQXX_HAVE_CHARCONV_FLOAT" >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_CHARCONV_INT" >&5 -printf %s "checking PQXX_HAVE_CHARCONV_INT... " >&6; } -PQXX_HAVE_CHARCONV_INT=yes -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -// Test for std::to_string/std::from_string for integral types. - -#include - -#include - - - -int main() - -{ - - char z[100]; - - auto rt = std::to_chars(std::begin(z), std::end(z), 9ULL); - - if (rt.ec != std::errc{}) - - return 1; - - unsigned long long n; - - auto rf = std::from_chars(std::cbegin(z), std::cend(z), n); - - if (rf.ec != std::errc{}) - - return 2; - - return (n == 9ULL) ? 0 : 1; - -} - - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO" -then : - -printf "%s\n" "#define PQXX_HAVE_CHARCONV_INT 1" >>confdefs.h - -else $as_nop - PQXX_HAVE_CHARCONV_INT=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_CHARCONV_INT" >&5 -printf "%s\n" "$PQXX_HAVE_CHARCONV_INT" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_CMP" >&5 printf %s "checking PQXX_HAVE_CMP... " >&6; } PQXX_HAVE_CMP=yes diff --git a/include/pqxx/config.h.in b/include/pqxx/config.h.in index fdca38cf9..fa6d215d3 100644 --- a/include/pqxx/config.h.in +++ b/include/pqxx/config.h.in @@ -63,9 +63,6 @@ /* Define if this feature is available. */ #undef PQXX_HAVE_CHARCONV_FLOAT -/* Define if this feature is available. */ -#undef PQXX_HAVE_CHARCONV_INT - /* Define if this feature is available. */ #undef PQXX_HAVE_CMP diff --git a/pqxx_cxx_feature_checks.ac b/pqxx_cxx_feature_checks.ac index 325608f29..7e97e32f2 100644 --- a/pqxx_cxx_feature_checks.ac +++ b/pqxx_cxx_feature_checks.ac @@ -19,16 +19,6 @@ AC_COMPILE_IFELSE( [Define if this feature is available.]), PQXX_HAVE_CHARCONV_FLOAT=no) AC_MSG_RESULT($PQXX_HAVE_CHARCONV_FLOAT) -AC_MSG_CHECKING([PQXX_HAVE_CHARCONV_INT]) -PQXX_HAVE_CHARCONV_INT=yes -AC_COMPILE_IFELSE( - [read_test(PQXX_HAVE_CHARCONV_INT.cxx)], - AC_DEFINE( - [PQXX_HAVE_CHARCONV_INT], - 1, - [Define if this feature is available.]), - PQXX_HAVE_CHARCONV_INT=no) -AC_MSG_RESULT($PQXX_HAVE_CHARCONV_INT) AC_MSG_CHECKING([PQXX_HAVE_CMP]) PQXX_HAVE_CMP=yes AC_COMPILE_IFELSE( diff --git a/src/strconv.cxx b/src/strconv.cxx index b460445ee..7fe7fba2d 100644 --- a/src/strconv.cxx +++ b/src/strconv.cxx @@ -118,11 +118,9 @@ template constexpr inline char *bottom_to_buf(char *end) } -#if defined(PQXX_HAVE_CHARCONV_INT) || defined(PQXX_HAVE_CHARCONV_FLOAT) /// Call to_chars, report errors as exceptions, add zero, return pointer. template -[[maybe_unused]] inline char * -wrap_to_chars(char *begin, char *end, T const &value) +inline char *wrap_to_chars(char *begin, char *end, T const &value) { auto res{std::to_chars(begin, end - 1, value)}; if (res.ec != std::errc()) @@ -144,7 +142,6 @@ wrap_to_chars(char *begin, char *end, T const &value) *res.ptr++ = '\0'; return res.ptr; } -#endif } // namespace @@ -196,13 +193,9 @@ template zview integral_traits::to_buf( template char *integral_traits::into_buf(char *begin, char *end, T const &value) { -#if defined(PQXX_HAVE_CHARCONV_INT) // This is exactly what to_chars is good at. Trust standard library // implementers to optimise better than we can. return wrap_to_chars(begin, end, value); -#else - return generic_into_buf(begin, end, value); -#endif } @@ -289,9 +282,7 @@ std::string PQXX_COLD state_buffer_overrun(int have_bytes, int need_bytes) namespace { -#if defined(PQXX_HAVE_CHARCONV_INT) || defined(PQXX_HAVE_CHARCONV_FLOAT) -template -[[maybe_unused]] inline TYPE from_string_arithmetic(std::string_view in) +template inline TYPE from_string_arithmetic(std::string_view in) { char const *here{std::data(in)}; auto const end{std::data(in) + std::size(in)}; @@ -332,162 +323,6 @@ template else throw pqxx::conversion_error{base + ": " + msg}; } -#endif -} // namespace - - -namespace -{ -#if !defined(PQXX_HAVE_CHARCONV_INT) -[[noreturn, maybe_unused]] void PQXX_COLD report_overflow() -{ - throw pqxx::conversion_error{ - "Could not convert string to integer: value out of range."}; -} - -template struct numeric_ten -{ - static inline constexpr T value = 10; -}; - -template struct numeric_high_threshold -{ - static inline constexpr T value = - (std::numeric_limits::max)() / numeric_ten::value; -}; - -template struct numeric_low_threshold -{ - static inline constexpr T value = - (std::numeric_limits::min)() / numeric_ten::value; -}; - -/// Return 10*n, or throw exception if it overflows. -template -[[maybe_unused]] constexpr inline T safe_multiply_by_ten(T n) -{ - using limits = std::numeric_limits; - - if (n > numeric_high_threshold::value) - PQXX_UNLIKELY - report_overflow(); - if constexpr (limits::is_signed) - { - if (numeric_low_threshold::value > n) - PQXX_UNLIKELY - report_overflow(); - } - return T(n * numeric_ten::value); -} - - -/// Add digit d to nonnegative n, or throw exception if it overflows. -template -[[maybe_unused]] constexpr inline T safe_add_digit(T n, T d) -{ - T const high_threshold{static_cast(std::numeric_limits::max() - d)}; - if (n > high_threshold) - PQXX_UNLIKELY - report_overflow(); - return static_cast(n + d); -} - - -/// Subtract digit d to nonpositive n, or throw exception if it overflows. -template -[[maybe_unused]] constexpr inline T safe_sub_digit(T n, T d) -{ - T const low_threshold{static_cast(std::numeric_limits::min() + d)}; - if (n < low_threshold) - PQXX_UNLIKELY - report_overflow(); - return static_cast(n - d); -} - - -/// For use in string parsing: add new numeric digit to intermediate value. -template -[[maybe_unused]] constexpr inline L absorb_digit_positive(L value, R digit) -{ - return safe_add_digit(safe_multiply_by_ten(value), L(digit)); -} - - -/// For use in string parsing: subtract digit from intermediate value. -template -[[maybe_unused]] constexpr inline L absorb_digit_negative(L value, R digit) -{ - return safe_sub_digit(safe_multiply_by_ten(value), L(digit)); -} - - -template -[[maybe_unused]] constexpr T from_string_integer(std::string_view text) -{ - if (std::size(text) == 0) - throw pqxx::conversion_error{ - "Attempt to convert empty string to " + pqxx::type_name + "."}; - - char const *const data{std::data(text)}; - std::size_t i{0}; - - // Skip whitespace. This is not the proper way to do it, but I see no way - // that any of the supported encodings could ever produce a valid character - // whose byte sequence would confuse this code. - // - // Why skip whitespace? Because that's how integral conversions are meant to - // work _for composite types._ I see no clean way to support leading - // whitespace there without putting the code in here. A shame about the - // overhead, modest as it is, for the normal case. - for (; i < std::size(text) and (data[i] == ' ' or data[i] == '\t'); ++i); - if (i == std::size(text)) - throw pqxx::conversion_error{ - "Converting string to " + pqxx::type_name + - ", but it contains only whitespace."}; - - char const initial{data[i]}; - T result{0}; - - if (pqxx::internal::is_digit(initial)) - { - for (; pqxx::internal::is_digit(data[i]); ++i) - result = absorb_digit_positive( - result, pqxx::internal::digit_to_number(data[i])); - } - else if (initial == '-') - { - if constexpr (not std::is_signed_v) - throw pqxx::conversion_error{ - "Attempt to convert negative value to " + pqxx::type_name + "."}; - - ++i; - if (i >= std::size(text)) - throw pqxx::conversion_error{ - "Converting string to " + pqxx::type_name + - ", but it contains only a sign."}; - for (; i < std::size(text) and pqxx::internal::is_digit(data[i]); ++i) - result = absorb_digit_negative( - result, pqxx::internal::digit_to_number(data[i])); - } - else - { - throw pqxx::conversion_error{ - "Could not convert string to " + pqxx::type_name + - ": " - "'" + - std::string{text} + "'."}; - } - - if (i < std::size(text)) - throw pqxx::conversion_error{ - "Unexpected text after " + pqxx::type_name + - ": " - "'" + - std::string{text} + "'."}; - - return result; -} -#endif // !PQXX_HAVE_CHARCONV_INT } // namespace @@ -711,11 +546,7 @@ namespace pqxx::internal { template T integral_traits::from_string(std::string_view text) { -#if defined(PQXX_HAVE_CHARCONV_INT) return from_string_arithmetic(text); -#else - return from_string_integer(text); -#endif } template short integral_traits::from_string(std::string_view); From 45008acc13ca06d5b5cd86e12187ccbcc7e62799 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 01:16:51 +0100 Subject: [PATCH 03/57] Retire checks for missing `std::cmp_less` etc. --- cmake/pqxx_cxx_feature_checks.cmake | 4 -- config-tests/PQXX_HAVE_CMP.cxx | 9 ---- configitems | 1 - configure | 36 --------------- include/pqxx/binarystring.hxx | 2 +- include/pqxx/config.h.in | 3 -- include/pqxx/internal/conversions.hxx | 18 ++++---- include/pqxx/largeobject.hxx | 2 +- include/pqxx/util.hxx | 64 +-------------------------- pqxx_cxx_feature_checks.ac | 10 ----- src/largeobject.cxx | 2 +- test/test_types.hxx | 2 +- test/unit/test_errorhandler.cxx | 2 +- 13 files changed, 15 insertions(+), 140 deletions(-) delete mode 100644 config-tests/PQXX_HAVE_CMP.cxx diff --git a/cmake/pqxx_cxx_feature_checks.cmake b/cmake/pqxx_cxx_feature_checks.cmake index 4b498620a..8fc7aa668 100644 --- a/cmake/pqxx_cxx_feature_checks.cmake +++ b/cmake/pqxx_cxx_feature_checks.cmake @@ -7,10 +7,6 @@ try_compile( PQXX_HAVE_CHARCONV_FLOAT ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CHARCONV_FLOAT.cxx ) -try_compile( - PQXX_HAVE_CMP ${PROJECT_BINARY_DIR} - SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CMP.cxx -) try_compile( PQXX_HAVE_CONCEPTS ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CONCEPTS.cxx diff --git a/config-tests/PQXX_HAVE_CMP.cxx b/config-tests/PQXX_HAVE_CMP.cxx deleted file mode 100644 index cd3f53b2b..000000000 --- a/config-tests/PQXX_HAVE_CMP.cxx +++ /dev/null @@ -1,9 +0,0 @@ -// Test for C++20 std::cmp_greater etc. support. -// C++20: Assume support. -#include - - -int main() -{ - return std::cmp_greater(-1, 2u) && std::cmp_less_equal(3, 0); -} diff --git a/configitems b/configitems index 757e9015d..0a85b9a24 100644 --- a/configitems +++ b/configitems @@ -6,7 +6,6 @@ PACKAGE_TARNAME internal autotools PACKAGE_VERSION internal autotools PQXX_HAVE_ASSUME public compiler PQXX_HAVE_CHARCONV_FLOAT internal compiler -PQXX_HAVE_CMP public compiler PQXX_HAVE_CONCEPTS public compiler PQXX_HAVE_CXA_DEMANGLE internal compiler PQXX_HAVE_GCC_PURE public compiler diff --git a/configure b/configure index 2a48b885d..2951c0f84 100755 --- a/configure +++ b/configure @@ -17324,42 +17324,6 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_CHARCONV_FLOAT" >&5 printf "%s\n" "$PQXX_HAVE_CHARCONV_FLOAT" >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_CMP" >&5 -printf %s "checking PQXX_HAVE_CMP... " >&6; } -PQXX_HAVE_CMP=yes -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -// Test for C++20 std::cmp_greater etc. support. - -// C++20: Assume support. - -#include - - - - - -int main() - -{ - - return std::cmp_greater(-1, 2u) && std::cmp_less_equal(3, 0); - -} - - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO" -then : - -printf "%s\n" "#define PQXX_HAVE_CMP 1" >>confdefs.h - -else $as_nop - PQXX_HAVE_CMP=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_CMP" >&5 -printf "%s\n" "$PQXX_HAVE_CMP" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_CONCEPTS" >&5 printf %s "checking PQXX_HAVE_CONCEPTS... " >&6; } PQXX_HAVE_CONCEPTS=yes diff --git a/include/pqxx/binarystring.hxx b/include/pqxx/binarystring.hxx index ab3ebe8f9..fe113a42d 100644 --- a/include/pqxx/binarystring.hxx +++ b/include/pqxx/binarystring.hxx @@ -212,7 +212,7 @@ template<> struct string_traits static char *into_buf(char *begin, char *end, binarystring const &value) { auto const budget{size_buffer(value)}; - if (internal::cmp_less(end - begin, budget)) + if (std::cmp_less(end - begin, budget)) throw conversion_overrun{ "Not enough buffer space to escape binary data."}; std::string_view text{value.view()}; diff --git a/include/pqxx/config.h.in b/include/pqxx/config.h.in index fa6d215d3..c4bade597 100644 --- a/include/pqxx/config.h.in +++ b/include/pqxx/config.h.in @@ -63,9 +63,6 @@ /* Define if this feature is available. */ #undef PQXX_HAVE_CHARCONV_FLOAT -/* Define if this feature is available. */ -#undef PQXX_HAVE_CMP - /* Define if this feature is available. */ #undef PQXX_HAVE_CONCEPTS diff --git a/include/pqxx/internal/conversions.hxx b/include/pqxx/internal/conversions.hxx index 462c2d5ce..57bc370e9 100644 --- a/include/pqxx/internal/conversions.hxx +++ b/include/pqxx/internal/conversions.hxx @@ -98,7 +98,7 @@ inline char *generic_into_buf(char *begin, char *end, T const &value) auto const space{end - begin}; // Include the trailing zero. auto const len = std::size(text) + 1; - if (internal::cmp_greater(len, space)) + if (std::cmp_greater(len, space)) throw conversion_overrun{ "Not enough buffer space to insert " + type_name + ". " + state_buffer_overrun(space, len)}; @@ -586,7 +586,7 @@ template struct string_traits static char *into_buf(char *begin, char *end, char const (&value)[N]) { - if (internal::cmp_less(end - begin, size_buffer(value))) + if (std::cmp_less(end - begin, size_buffer(value))) throw conversion_overrun{ "Could not convert char[] to string: too long for buffer."}; std::memcpy(begin, value, N); @@ -618,7 +618,7 @@ template<> struct string_traits static char *into_buf(char *begin, char *end, std::string const &value) { - if (internal::cmp_greater_equal(std::size(value), end - begin)) + if (std::cmp_greater_equal(std::size(value), end - begin)) throw conversion_overrun{ "Could not convert string to string: too long for buffer."}; // Include the trailing zero. @@ -661,7 +661,7 @@ template<> struct string_traits static char *into_buf(char *begin, char *end, std::string_view const &value) { - if (internal::cmp_greater_equal(std::size(value), end - begin)) + if (std::cmp_greater_equal(std::size(value), end - begin)) throw conversion_overrun{ "Could not store string_view: too long for buffer."}; value.copy(begin, std::size(value)); @@ -700,7 +700,7 @@ template<> struct string_traits static char *into_buf(char *begin, char *end, zview const &value) { auto const size{std::size(value)}; - if (internal::cmp_less_equal(end - begin, std::size(value))) + if (std::cmp_less_equal(end - begin, std::size(value))) throw conversion_overrun{"Not enough buffer space to store this zview."}; value.copy(begin, size); begin[size] = '\0'; @@ -928,7 +928,7 @@ template struct string_traits static char *into_buf(char *begin, char *end, DATA const &value) { auto const budget{size_buffer(value)}; - if (internal::cmp_less(end - begin, budget)) + if (std::cmp_less(end - begin, budget)) throw conversion_overrun{ "Not enough buffer space to escape binary data."}; internal::esc_bin(value, begin); @@ -965,7 +965,7 @@ template<> struct string_traits static char *into_buf(char *begin, char *end, bytes const &value) { auto const budget{size_buffer(value)}; - if (internal::cmp_less(end - begin, budget)) + if (std::cmp_less(end - begin, budget)) throw conversion_overrun{ "Not enough buffer space to escape binary data."}; internal::esc_bin(value, begin); @@ -1011,7 +1011,7 @@ template<> struct string_traits static char *into_buf(char *begin, char *end, bytes_view const &value) { auto const budget{size_buffer(value)}; - if (internal::cmp_less(end - begin, budget)) + if (std::cmp_less(end - begin, budget)) throw conversion_overrun{ "Not enough buffer space to escape binary data."}; internal::esc_bin(value, begin); @@ -1052,7 +1052,7 @@ public: { assert(begin <= end); std::size_t const budget{size_buffer(value)}; - if (internal::cmp_less(end - begin, budget)) + if (std::cmp_less(end - begin, budget)) throw conversion_overrun{ "Not enough buffer space to convert array to string."}; diff --git a/include/pqxx/largeobject.hxx b/include/pqxx/largeobject.hxx index 6439826b6..379157a64 100644 --- a/include/pqxx/largeobject.hxx +++ b/include/pqxx/largeobject.hxx @@ -447,7 +447,7 @@ protected: auto const write_sz{pp - pb}; auto const written_sz{ m_obj.cwrite(pb, static_cast(pp - pb))}; - if (internal::cmp_less_equal(written_sz, 0)) + if (std::cmp_less_equal(written_sz, 0)) throw internal_error{ "pqxx::largeobject: write failed " "(is transaction still valid on write or flush?), " diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index 5ed0496b3..acfc873ce 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -58,68 +58,6 @@ namespace pqxx /// Internal items for libpqxx' own use. Do not use these yourself. namespace pqxx::internal { - -// C++20: Retire wrapper. -/// Same as `std::cmp_less`, or a workaround where that's not available. -template -inline constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept -{ -#if defined(PQXX_HAVE_CMP) - return std::cmp_less(lhs, rhs); -#else - // We need a variable just because lgtm.com gives off a false positive - // warning when we compare the values directly. It considers that a - // "self-comparison." - constexpr bool left_signed{std::is_signed_v}; - if constexpr (left_signed == std::is_signed_v) - return lhs < rhs; - else if constexpr (std::is_signed_v) - return (lhs <= 0) ? true : (std::make_unsigned_t(lhs) < rhs); - else - return (rhs <= 0) ? false : (lhs < std::make_unsigned_t(rhs)); -#endif -} - - -// C++20: Retire wrapper. -/// C++20 std::cmp_greater, or workaround if not available. -template -inline constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept -{ -#if defined(PQXX_HAVE_CMP) - return std::cmp_greater(lhs, rhs); -#else - return cmp_less(rhs, lhs); -#endif -} - - -// C++20: Retire wrapper. -/// C++20 std::cmp_less_equal, or workaround if not available. -template -inline constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept -{ -#if defined(PQXX_HAVE_CMP) - return std::cmp_less_equal(lhs, rhs); -#else - return not cmp_less(rhs, lhs); -#endif -} - - -// C++20: Retire wrapper. -/// C++20 std::cmp_greater_equal, or workaround if not available. -template -inline constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept -{ -#if defined(PQXX_HAVE_CMP) - return std::cmp_greater_equal(lhs, rhs); -#else - return not cmp_less(lhs, rhs); -#endif -} - - /// Efficiently concatenate two strings. /** This is a special case of concatenate(), needed because dependency * management does not let us use that function here. @@ -198,7 +136,7 @@ inline TO check_cast(FROM value, std::string_view description) constexpr auto to_max{static_cast((to_limits::max)())}; if constexpr (from_max > to_max) { - if (internal::cmp_greater(value, to_max)) + if (std::cmp_greater(value, to_max)) throw range_error{internal::cat2("Cast overflow: "sv, description)}; } } diff --git a/pqxx_cxx_feature_checks.ac b/pqxx_cxx_feature_checks.ac index 7e97e32f2..195ff71ca 100644 --- a/pqxx_cxx_feature_checks.ac +++ b/pqxx_cxx_feature_checks.ac @@ -19,16 +19,6 @@ AC_COMPILE_IFELSE( [Define if this feature is available.]), PQXX_HAVE_CHARCONV_FLOAT=no) AC_MSG_RESULT($PQXX_HAVE_CHARCONV_FLOAT) -AC_MSG_CHECKING([PQXX_HAVE_CMP]) -PQXX_HAVE_CMP=yes -AC_COMPILE_IFELSE( - [read_test(PQXX_HAVE_CMP.cxx)], - AC_DEFINE( - [PQXX_HAVE_CMP], - 1, - [Define if this feature is available.]), - PQXX_HAVE_CMP=no) -AC_MSG_RESULT($PQXX_HAVE_CMP) AC_MSG_CHECKING([PQXX_HAVE_CONCEPTS]) PQXX_HAVE_CONCEPTS=yes AC_COMPILE_IFELSE( diff --git a/src/largeobject.cxx b/src/largeobject.cxx index 4adecf72d..94572d466 100644 --- a/src/largeobject.cxx +++ b/src/largeobject.cxx @@ -237,7 +237,7 @@ pqxx::largeobjectaccess::write(char const buf[], std::size_t len) { if (id() == oid_none) throw usage_error{"No object selected."}; - if (auto const bytes{cwrite(buf, len)}; internal::cmp_less(bytes, len)) + if (auto const bytes{cwrite(buf, len)}; std::cmp_less(bytes, len)) { int const err{errno}; if (err == ENOMEM) diff --git a/test/test_types.hxx b/test/test_types.hxx index e655e7f1f..ebf53c94e 100644 --- a/test/test_types.hxx +++ b/test/test_types.hxx @@ -117,7 +117,7 @@ template<> struct string_traits static char *into_buf(char *begin, char *end, ipv4 const &value) { - if (pqxx::internal::cmp_less(end - begin, size_buffer(value))) + if (std::cmp_less(end - begin, size_buffer(value))) throw conversion_error{"Buffer too small for ipv4."}; char *here = begin; for (int i = 0; i < 4; ++i) diff --git a/test/unit/test_errorhandler.cxx b/test/unit/test_errorhandler.cxx index 9dc27a806..b6cd5d70a 100644 --- a/test/unit/test_errorhandler.cxx +++ b/test/unit/test_errorhandler.cxx @@ -63,7 +63,7 @@ template<> struct string_traits static char *into_buf(char *begin, char *end, TestErrorHandler *const &value) { std::string text{"TestErrorHandler at " + pqxx::to_string(value)}; - if (pqxx::internal::cmp_greater_equal(std::size(text), end - begin)) + if (std::cmp_greater_equal(std::size(text), end - begin)) throw conversion_overrun{"Not enough buffer for TestErrorHandler."}; std::memcpy(begin, text.c_str(), std::size(text) + 1); return begin + std::size(text) + 1; From a6572632cf8bc42feaaadb6d3c384fed1166c9d4 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 01:41:56 +0100 Subject: [PATCH 04/57] Assume support for `[[likely]]`/`[[unlikely]]`. --- cmake/pqxx_cxx_feature_checks.cmake | 4 - config-tests/PQXX_HAVE_LIKELY.cxx | 14 -- configitems | 1 - configure | 46 ---- include/pqxx/config.h.in | 3 - include/pqxx/field.hxx | 6 +- include/pqxx/internal/encodings.hxx | 220 ++++++++------------ include/pqxx/internal/header-pre.hxx | 10 - include/pqxx/internal/stream_query_impl.hxx | 4 +- include/pqxx/params.hxx | 3 +- include/pqxx/pipeline.hxx | 2 +- include/pqxx/stream_from.hxx | 8 +- include/pqxx/util.hxx | 4 +- pqxx_cxx_feature_checks.ac | 10 - src/array.cxx | 10 +- src/connection.cxx | 94 ++++----- src/cursor.cxx | 10 +- src/encodings.cxx | 43 ++-- src/pipeline.cxx | 32 ++- src/result.cxx | 19 +- src/robusttransaction.cxx | 12 +- src/strconv.cxx | 35 ++-- src/stream_from.cxx | 20 +- src/stream_to.cxx | 2 +- src/time.cxx | 16 +- src/transaction_base.cxx | 20 +- src/util.cxx | 17 +- 27 files changed, 239 insertions(+), 426 deletions(-) delete mode 100644 config-tests/PQXX_HAVE_LIKELY.cxx diff --git a/cmake/pqxx_cxx_feature_checks.cmake b/cmake/pqxx_cxx_feature_checks.cmake index 8fc7aa668..d9ac004c1 100644 --- a/cmake/pqxx_cxx_feature_checks.cmake +++ b/cmake/pqxx_cxx_feature_checks.cmake @@ -23,10 +23,6 @@ try_compile( PQXX_HAVE_GCC_VISIBILITY ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_GCC_VISIBILITY.cxx ) -try_compile( - PQXX_HAVE_LIKELY ${PROJECT_BINARY_DIR} - SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_LIKELY.cxx -) try_compile( PQXX_HAVE_MULTIDIM ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_MULTIDIM.cxx diff --git a/config-tests/PQXX_HAVE_LIKELY.cxx b/config-tests/PQXX_HAVE_LIKELY.cxx deleted file mode 100644 index 4e57e5e87..000000000 --- a/config-tests/PQXX_HAVE_LIKELY.cxx +++ /dev/null @@ -1,14 +0,0 @@ -// Test for C++20 [[likely]] and [[unlikely]] attributes. -// C++20: Assume support. - -#if !__has_cpp_attribute(likely) -# error "No support for [[likely]] / [[unlikely]] attributes." -#endif - -int foo(int i) -{ - if (i > 0) [[likely]] - return 100; - else - return 0; -} diff --git a/configitems b/configitems index 0a85b9a24..bbac7c1f2 100644 --- a/configitems +++ b/configitems @@ -11,7 +11,6 @@ PQXX_HAVE_CXA_DEMANGLE internal compiler PQXX_HAVE_GCC_PURE public compiler PQXX_HAVE_GCC_VISIBILITY public compiler PQXX_HAVE_MULTIDIM public compiler -PQXX_HAVE_LIKELY public compiler PQXX_HAVE_PATH public compiler PQXX_HAVE_POLL internal compiler PQXX_HAVE_SLEEP_FOR internal compiler diff --git a/configure b/configure index 2951c0f84..57860a64a 100755 --- a/configure +++ b/configure @@ -17500,52 +17500,6 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_GCC_VISIBILITY" >&5 printf "%s\n" "$PQXX_HAVE_GCC_VISIBILITY" >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_LIKELY" >&5 -printf %s "checking PQXX_HAVE_LIKELY... " >&6; } -PQXX_HAVE_LIKELY=yes -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -// Test for C++20 [[likely]] and [[unlikely]] attributes. - -// C++20: Assume support. - - - -#if !__has_cpp_attribute(likely) - -# error "No support for [[likely]] / [[unlikely]] attributes." - -#endif - - - -int foo(int i) - -{ - - if (i > 0) [[likely]] - - return 100; - - else - - return 0; - -} - - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO" -then : - -printf "%s\n" "#define PQXX_HAVE_LIKELY 1" >>confdefs.h - -else $as_nop - PQXX_HAVE_LIKELY=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_LIKELY" >&5 -printf "%s\n" "$PQXX_HAVE_LIKELY" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_MULTIDIM" >&5 printf %s "checking PQXX_HAVE_MULTIDIM... " >&6; } PQXX_HAVE_MULTIDIM=yes diff --git a/include/pqxx/config.h.in b/include/pqxx/config.h.in index c4bade597..26253f1a3 100644 --- a/include/pqxx/config.h.in +++ b/include/pqxx/config.h.in @@ -75,9 +75,6 @@ /* Define if this feature is available. */ #undef PQXX_HAVE_GCC_VISIBILITY -/* Define if this feature is available. */ -#undef PQXX_HAVE_LIKELY - /* Define if this feature is available. */ #undef PQXX_HAVE_MULTIDIM diff --git a/include/pqxx/field.hxx b/include/pqxx/field.hxx index 22475cc8f..84f645611 100644 --- a/include/pqxx/field.hxx +++ b/include/pqxx/field.hxx @@ -374,8 +374,7 @@ inline bool field::to( template<> inline std::string_view field::as() const { if (is_null()) - PQXX_UNLIKELY - internal::throw_null_conversion(type_name); + internal::throw_null_conversion(type_name); return view(); } @@ -412,8 +411,7 @@ inline bool field::to(zview &obj, zview const &default_value) const template<> inline zview field::as() const { if (is_null()) - PQXX_UNLIKELY - internal::throw_null_conversion(type_name); + internal::throw_null_conversion(type_name); return zview{c_str(), size()}; } diff --git a/include/pqxx/internal/encodings.hxx b/include/pqxx/internal/encodings.hxx index 04b8c1788..92aa341fc 100644 --- a/include/pqxx/internal/encodings.hxx +++ b/include/pqxx/internal/encodings.hxx @@ -235,8 +235,8 @@ template<> struct glyph_scanner call(char const /* buffer */[], std::size_t buffer_len, std::size_t start) { // TODO: Don't bother with npos. Let the caller check. - if (start >= buffer_len) - PQXX_UNLIKELY return std::string::npos; + if (start >= buffer_len) [[unlikely]] + return std::string::npos; else return start + 1; } @@ -249,23 +249,22 @@ template<> struct glyph_scanner static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start) { - if (start >= buffer_len) - PQXX_UNLIKELY return std::string::npos; + if (start >= buffer_len) [[unlikely]] + return std::string::npos; auto const byte1{get_byte(buffer, start)}; if (byte1 < 0x80) return start + 1; if (not between_inc(byte1, 0x81, 0xfe) or (start + 2 > buffer_len)) - PQXX_UNLIKELY - throw_for_encoding_error("BIG5", buffer, start, 1); + [[unlikely]] + throw_for_encoding_error("BIG5", buffer, start, 1); auto const byte2{get_byte(buffer, start + 1)}; if ( not between_inc(byte2, 0x40, 0x7e) and - not between_inc(byte2, 0xa1, 0xfe)) - PQXX_UNLIKELY - throw_for_encoding_error("BIG5", buffer, start, 2); + not between_inc(byte2, 0xa1, 0xfe)) [[unlikely]] + throw_for_encoding_error("BIG5", buffer, start, 2); return start + 2; } @@ -297,13 +296,12 @@ template<> struct glyph_scanner return start + 1; if (not between_inc(byte1, 0xa1, 0xf7) or start + 2 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("EUC_CN", buffer, start, 1); + [[unlikely]] + throw_for_encoding_error("EUC_CN", buffer, start, 1); auto const byte2{get_byte(buffer, start + 1)}; - if (not between_inc(byte2, 0xa1, 0xfe)) - PQXX_UNLIKELY - throw_for_encoding_error("EUC_CN", buffer, start, 2); + if (not between_inc(byte2, 0xa1, 0xfe)) [[unlikely]] + throw_for_encoding_error("EUC_CN", buffer, start, 2); return start + 2; } @@ -327,25 +325,22 @@ template<> struct glyph_scanner if (byte1 < 0x80) return start + 1; - if (start + 2 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("EUC_JP", buffer, start, 1); + if (start + 2 > buffer_len) [[unlikely]] + throw_for_encoding_error("EUC_JP", buffer, start, 1); auto const byte2{get_byte(buffer, start + 1)}; if (byte1 == 0x8e) { - if (not between_inc(byte2, 0xa1, 0xfe)) - PQXX_UNLIKELY - throw_for_encoding_error("EUC_JP", buffer, start, 2); + if (not between_inc(byte2, 0xa1, 0xfe)) [[unlikely]] + throw_for_encoding_error("EUC_JP", buffer, start, 2); return start + 2; } if (between_inc(byte1, 0xa1, 0xfe)) { - if (not between_inc(byte2, 0xa1, 0xfe)) - PQXX_UNLIKELY - throw_for_encoding_error("EUC_JP", buffer, start, 2); + if (not between_inc(byte2, 0xa1, 0xfe)) [[unlikely]] + throw_for_encoding_error("EUC_JP", buffer, start, 2); return start + 2; } @@ -355,9 +350,8 @@ template<> struct glyph_scanner auto const byte3{get_byte(buffer, start + 2)}; if ( not between_inc(byte2, 0xa1, 0xfe) or - not between_inc(byte3, 0xa1, 0xfe)) - PQXX_UNLIKELY - throw_for_encoding_error("EUC_JP", buffer, start, 3); + not between_inc(byte3, 0xa1, 0xfe)) [[unlikely]] + throw_for_encoding_error("EUC_JP", buffer, start, 3); return start + 3; } @@ -373,21 +367,20 @@ template<> struct glyph_scanner static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start) { - if (start >= buffer_len) - PQXX_UNLIKELY return std::string::npos; + if (start >= buffer_len) [[unlikely]] + return std::string::npos; auto const byte1{get_byte(buffer, start)}; if (byte1 < 0x80) return start + 1; if (not between_inc(byte1, 0xa1, 0xfe) or start + 2 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("EUC_KR", buffer, start, 1); + [[unlikely]] + throw_for_encoding_error("EUC_KR", buffer, start, 1); auto const byte2{get_byte(buffer, start + 1)}; - if (not between_inc(byte2, 0xa1, 0xfe)) - PQXX_UNLIKELY - throw_for_encoding_error("EUC_KR", buffer, start, 1); + if (not between_inc(byte2, 0xa1, 0xfe)) [[unlikely]] + throw_for_encoding_error("EUC_KR", buffer, start, 1); return start + 2; } @@ -400,31 +393,27 @@ template<> struct glyph_scanner static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start) { - if (start >= buffer_len) - PQXX_UNLIKELY - return std::string::npos; + if (start >= buffer_len) [[unlikely]] + return std::string::npos; auto const byte1{get_byte(buffer, start)}; if (byte1 < 0x80) return start + 1; - if (start + 2 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("EUC_KR", buffer, start, 1); + if (start + 2 > buffer_len) [[unlikely]] + throw_for_encoding_error("EUC_KR", buffer, start, 1); auto const byte2{get_byte(buffer, start + 1)}; if (between_inc(byte1, 0xa1, 0xfe)) { - if (not between_inc(byte2, 0xa1, 0xfe)) - PQXX_UNLIKELY - throw_for_encoding_error("EUC_KR", buffer, start, 2); + if (not between_inc(byte2, 0xa1, 0xfe)) [[unlikely]] + throw_for_encoding_error("EUC_KR", buffer, start, 2); return start + 2; } - if (byte1 != 0x8e or start + 4 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("EUC_KR", buffer, start, 1); + if (byte1 != 0x8e or start + 4 > buffer_len) [[unlikely]] + throw_for_encoding_error("EUC_KR", buffer, start, 1); if ( between_inc(byte2, 0xa1, 0xb0) and @@ -432,8 +421,7 @@ template<> struct glyph_scanner between_inc(get_byte(buffer, start + 3), 0xa1, 0xfe)) return start + 4; - PQXX_UNLIKELY - throw_for_encoding_error("EUC_KR", buffer, start, 4); + [[unlikely]] throw_for_encoding_error("EUC_KR", buffer, start, 4); } }; @@ -444,8 +432,8 @@ template<> struct glyph_scanner static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start) { - if (start >= buffer_len) - PQXX_UNLIKELY return std::string::npos; + if (start >= buffer_len) [[unlikely]] + return std::string::npos; auto const byte1{get_byte(buffer, start)}; if (byte1 < 0x80) @@ -453,23 +441,20 @@ template<> struct glyph_scanner if (byte1 == 0x80) throw_for_encoding_error("GB18030", buffer, start, buffer_len - start); - if (start + 2 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("GB18030", buffer, start, buffer_len - start); + if (start + 2 > buffer_len) [[unlikely]] + throw_for_encoding_error("GB18030", buffer, start, buffer_len - start); auto const byte2{get_byte(buffer, start + 1)}; if (between_inc(byte2, 0x40, 0xfe)) { - if (byte2 == 0x7f) - PQXX_UNLIKELY - throw_for_encoding_error("GB18030", buffer, start, 2); + if (byte2 == 0x7f) [[unlikely]] + throw_for_encoding_error("GB18030", buffer, start, 2); return start + 2; } - if (start + 4 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("GB18030", buffer, start, buffer_len - start); + if (start + 4 > buffer_len) [[unlikely]] + throw_for_encoding_error("GB18030", buffer, start, buffer_len - start); if ( between_inc(byte2, 0x30, 0x39) and @@ -477,8 +462,7 @@ template<> struct glyph_scanner between_inc(get_byte(buffer, start + 3), 0x30, 0x39)) return start + 4; - PQXX_UNLIKELY - throw_for_encoding_error("GB18030", buffer, start, 4); + [[unlikely]] throw_for_encoding_error("GB18030", buffer, start, 4); } }; @@ -489,16 +473,15 @@ template<> struct glyph_scanner static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start) { - if (start >= buffer_len) - PQXX_UNLIKELY return std::string::npos; + if (start >= buffer_len) [[unlikely]] + return std::string::npos; auto const byte1{get_byte(buffer, start)}; if (byte1 < 0x80) return start + 1; - if (start + 2 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("GBK", buffer, start, 1); + if (start + 2 > buffer_len) [[unlikely]] + throw_for_encoding_error("GBK", buffer, start, 1); auto const byte2{get_byte(buffer, start + 1)}; if ( @@ -516,8 +499,7 @@ template<> struct glyph_scanner byte2 != 0x7f)) return start + 2; - PQXX_UNLIKELY - throw_for_encoding_error("GBK", buffer, start, 2); + [[unlikely]] throw_for_encoding_error("GBK", buffer, start, 2); } }; @@ -536,16 +518,15 @@ template<> struct glyph_scanner static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start) { - if (start >= buffer_len) - PQXX_UNLIKELY return std::string::npos; + if (start >= buffer_len) [[unlikely]] + return std::string::npos; auto const byte1{get_byte(buffer, start)}; if (byte1 < 0x80) return start + 1; - if (start + 2 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("JOHAB", buffer, start, 1); + if (start + 2 > buffer_len) [[unlikely]] + throw_for_encoding_error("JOHAB", buffer, start, 1); auto const byte2{get_byte(buffer, start)}; if ( @@ -555,8 +536,7 @@ template<> struct glyph_scanner (between_inc(byte2, 0x31, 0x7e) or between_inc(byte2, 0x91, 0xfe)))) return start + 2; - PQXX_UNLIKELY - throw_for_encoding_error("JOHAB", buffer, start, 2); + [[unlikely]] throw_for_encoding_error("JOHAB", buffer, start, 2); } }; @@ -573,24 +553,22 @@ template<> struct glyph_scanner static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start) { - if (start >= buffer_len) - PQXX_UNLIKELY return std::string::npos; + if (start >= buffer_len) [[unlikely]] + return std::string::npos; auto const byte1{get_byte(buffer, start)}; if (byte1 < 0x80) return start + 1; - if (start + 2 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("MULE_INTERNAL", buffer, start, 1); + if (start + 2 > buffer_len) [[unlikely]] + throw_for_encoding_error("MULE_INTERNAL", buffer, start, 1); auto const byte2{get_byte(buffer, start + 1)}; if (between_inc(byte1, 0x81, 0x8d) and byte2 >= 0xa0) return start + 2; - if (start + 3 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("MULE_INTERNAL", buffer, start, 2); + if (start + 3 > buffer_len) [[unlikely]] + throw_for_encoding_error("MULE_INTERNAL", buffer, start, 2); if ( ((byte1 == 0x9a and between_inc(byte2, 0xa0, 0xdf)) or @@ -599,9 +577,8 @@ template<> struct glyph_scanner (byte2 >= 0xa0)) return start + 3; - if (start + 4 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("MULE_INTERNAL", buffer, start, 3); + if (start + 4 > buffer_len) [[unlikely]] + throw_for_encoding_error("MULE_INTERNAL", buffer, start, 3); if ( ((byte1 == 0x9c and between_inc(byte2, 0xf0, 0xf4)) or @@ -610,8 +587,7 @@ template<> struct glyph_scanner get_byte(buffer, start + 4) >= 0xa0) return start + 4; - PQXX_UNLIKELY - throw_for_encoding_error("MULE_INTERNAL", buffer, start, 4); + [[unlikely]] throw_for_encoding_error("MULE_INTERNAL", buffer, start, 4); } }; @@ -639,24 +615,20 @@ template<> struct glyph_scanner if ( not between_inc(byte1, 0x81, 0x9f) and - not between_inc(byte1, 0xe0, 0xfc)) - PQXX_UNLIKELY - throw_for_encoding_error("SJIS", buffer, start, 1); + not between_inc(byte1, 0xe0, 0xfc)) [[unlikely]] + throw_for_encoding_error("SJIS", buffer, start, 1); - if (start + 2 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("SJIS", buffer, start, buffer_len - start); + if (start + 2 > buffer_len) [[unlikely]] + throw_for_encoding_error("SJIS", buffer, start, buffer_len - start); auto const byte2{get_byte(buffer, start + 1)}; - if (byte2 == 0x7f) - PQXX_UNLIKELY - throw_for_encoding_error("SJIS", buffer, start, 2); + if (byte2 == 0x7f) [[unlikely]] + throw_for_encoding_error("SJIS", buffer, start, 2); if (between_inc(byte2, 0x40, 0x9e) or between_inc(byte2, 0x9f, 0xfc)) return start + 2; - PQXX_UNLIKELY - throw_for_encoding_error("SJIS", buffer, start, 2); + [[unlikely]] throw_for_encoding_error("SJIS", buffer, start, 2); } }; @@ -667,16 +639,15 @@ template<> struct glyph_scanner static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start) { - if (start >= buffer_len) - PQXX_UNLIKELY return std::string::npos; + if (start >= buffer_len) [[unlikely]] + return std::string::npos; auto const byte1{get_byte(buffer, start)}; if (byte1 < 0x80) return start + 1; - if (start + 2 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("UHC", buffer, start, buffer_len - start); + if (start + 2 > buffer_len) [[unlikely]] + throw_for_encoding_error("UHC", buffer, start, buffer_len - start); auto const byte2{get_byte(buffer, start + 1)}; if (between_inc(byte1, 0x80, 0xc6)) @@ -686,15 +657,13 @@ template<> struct glyph_scanner between_inc(byte2, 0x80, 0xfe)) return start + 2; - PQXX_UNLIKELY - throw_for_encoding_error("UHC", buffer, start, 2); + [[unlikely]] throw_for_encoding_error("UHC", buffer, start, 2); } if (between_inc(byte1, 0xa1, 0xfe)) { - if (not between_inc(byte2, 0xa1, 0xfe)) - PQXX_UNLIKELY - throw_for_encoding_error("UHC", buffer, start, 2); + if (not between_inc(byte2, 0xa1, 0xfe)) [[unlikely]] + throw_for_encoding_error("UHC", buffer, start, 2); return start + 2; } @@ -710,30 +679,27 @@ template<> struct glyph_scanner static PQXX_PURE std::size_t call(char const buffer[], std::size_t buffer_len, std::size_t start) { - if (start >= buffer_len) - PQXX_UNLIKELY return std::string::npos; + if (start >= buffer_len) [[unlikely]] + return std::string::npos; auto const byte1{get_byte(buffer, start)}; if (byte1 < 0x80) return start + 1; - if (start + 2 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("UTF8", buffer, start, buffer_len - start); + if (start + 2 > buffer_len) [[unlikely]] + throw_for_encoding_error("UTF8", buffer, start, buffer_len - start); auto const byte2{get_byte(buffer, start + 1)}; if (between_inc(byte1, 0xc0, 0xdf)) { - if (not between_inc(byte2, 0x80, 0xbf)) - PQXX_UNLIKELY - throw_for_encoding_error("UTF8", buffer, start, 2); + if (not between_inc(byte2, 0x80, 0xbf)) [[unlikely]] + throw_for_encoding_error("UTF8", buffer, start, 2); return start + 2; } - if (start + 3 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("UTF8", buffer, start, buffer_len - start); + if (start + 3 > buffer_len) [[unlikely]] + throw_for_encoding_error("UTF8", buffer, start, buffer_len - start); auto const byte3{get_byte(buffer, start + 2)}; if (between_inc(byte1, 0xe0, 0xef)) @@ -741,13 +707,11 @@ template<> struct glyph_scanner if (between_inc(byte2, 0x80, 0xbf) and between_inc(byte3, 0x80, 0xbf)) return start + 3; - PQXX_UNLIKELY - throw_for_encoding_error("UTF8", buffer, start, 3); + [[unlikely]] throw_for_encoding_error("UTF8", buffer, start, 3); } - if (start + 4 > buffer_len) - PQXX_UNLIKELY - throw_for_encoding_error("UTF8", buffer, start, buffer_len - start); + if (start + 4 > buffer_len) [[unlikely]] + throw_for_encoding_error("UTF8", buffer, start, buffer_len - start); if (between_inc(byte1, 0xf0, 0xf7)) { @@ -756,12 +720,10 @@ template<> struct glyph_scanner between_inc(get_byte(buffer, start + 3), 0x80, 0xbf)) return start + 4; - PQXX_UNLIKELY - throw_for_encoding_error("UTF8", buffer, start, 4); + [[unlikely]] throw_for_encoding_error("UTF8", buffer, start, 4); } - PQXX_UNLIKELY - throw_for_encoding_error("UTF8", buffer, start, 1); + [[unlikely]] throw_for_encoding_error("UTF8", buffer, start, 1); } }; @@ -798,7 +760,7 @@ map_ascii_search_group(encoding_group enc) noexcept // string byte for byte. Multibyte characters have the high bit set. return encoding_group::MONOBYTE; - default: PQXX_UNLIKELY return enc; + default: [[unlikely]] return enc; } } diff --git a/include/pqxx/internal/header-pre.hxx b/include/pqxx/internal/header-pre.hxx index 5c240d0b7..0b1eb80e3 100644 --- a/include/pqxx/internal/header-pre.hxx +++ b/include/pqxx/internal/header-pre.hxx @@ -170,16 +170,6 @@ # define PQXX_NOVTABLE /* novtable */ #endif -// C++20: Assume support. -#if defined(PQXX_HAVE_LIKELY) -# define PQXX_LIKELY [[likely]] -# define PQXX_UNLIKELY [[unlikely]] -#else -# define PQXX_LIKELY /* [[likely]] */ -# define PQXX_UNLIKELY /* [[unlikely]] */ -#endif - - // C++23: Assume support. #if defined(PQXX_HAVE_ASSUME) # define PQXX_ASSUME(condition) [[assume(condition)]] diff --git a/include/pqxx/internal/stream_query_impl.hxx b/include/pqxx/internal/stream_query_impl.hxx index 714efd1c0..829730f79 100644 --- a/include/pqxx/internal/stream_query_impl.hxx +++ b/include/pqxx/internal/stream_query_impl.hxx @@ -180,8 +180,8 @@ stream_query::read_line() & { auto line{gate.read_copy_line()}; // Check for completion. - if (not line.first) - PQXX_UNLIKELY close(); + if (not line.first) [[unlikely]] + close(); return line; } catch (std::exception const &) diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index e11075fbf..78d794a82 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -253,9 +253,8 @@ public: } else { - PQXX_LIKELY // Shortcut for the common case: just increment that last digit. - ++m_buf[m_len - 1]; + [[likely]]++ m_buf[m_len - 1]; } } diff --git a/include/pqxx/pipeline.hxx b/include/pqxx/pipeline.hxx index e8dbd8921..9d39933b1 100644 --- a/include/pqxx/pipeline.hxx +++ b/include/pqxx/pipeline.hxx @@ -195,7 +195,7 @@ private: /// The given query failed; never issue anything beyond that. void set_error_at(query_id qid) noexcept { - PQXX_UNLIKELY + [[unlikely]] if (qid < m_error) m_error = qid; } diff --git a/include/pqxx/stream_from.hxx b/include/pqxx/stream_from.hxx index 6a0687c38..ab538fa68 100644 --- a/include/pqxx/stream_from.hxx +++ b/include/pqxx/stream_from.hxx @@ -322,13 +322,13 @@ inline stream_from::stream_from( template inline stream_from &stream_from::operator>>(Tuple &t) { - if (m_finished) - PQXX_UNLIKELY return *this; + if (m_finished) [[unlikely]] + return *this; static constexpr auto tup_size{std::tuple_size_v}; m_fields.reserve(tup_size); parse_line(); - if (m_finished) - PQXX_UNLIKELY return *this; + if (m_finished) [[unlikely]] + return *this; if (std::size(m_fields) != tup_size) throw usage_error{internal::concat( diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index acfc873ce..6fb4126c8 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -573,9 +573,9 @@ inline constexpr char unescape_char(char escaped) noexcept switch (escaped) { case 'b': // Backspace. - PQXX_UNLIKELY return '\b'; + [[unlikely]] return '\b'; case 'f': // Form feed - PQXX_UNLIKELY return '\f'; + [[unlikely]] return '\f'; case 'n': // Line feed. return '\n'; case 'r': // Carriage return. diff --git a/pqxx_cxx_feature_checks.ac b/pqxx_cxx_feature_checks.ac index 195ff71ca..fd91792a3 100644 --- a/pqxx_cxx_feature_checks.ac +++ b/pqxx_cxx_feature_checks.ac @@ -59,16 +59,6 @@ AC_COMPILE_IFELSE( [Define if this feature is available.]), PQXX_HAVE_GCC_VISIBILITY=no) AC_MSG_RESULT($PQXX_HAVE_GCC_VISIBILITY) -AC_MSG_CHECKING([PQXX_HAVE_LIKELY]) -PQXX_HAVE_LIKELY=yes -AC_COMPILE_IFELSE( - [read_test(PQXX_HAVE_LIKELY.cxx)], - AC_DEFINE( - [PQXX_HAVE_LIKELY], - 1, - [Define if this feature is available.]), - PQXX_HAVE_LIKELY=no) -AC_MSG_RESULT($PQXX_HAVE_LIKELY) AC_MSG_CHECKING([PQXX_HAVE_MULTIDIM]) PQXX_HAVE_MULTIDIM=yes AC_COMPILE_IFELSE( diff --git a/src/array.cxx b/src/array.cxx index 6a5dacd54..5d5594ee4 100644 --- a/src/array.cxx +++ b/src/array.cxx @@ -135,8 +135,7 @@ std::pair array_parser::parse_array_step() { // The normal case: we just parsed an unquoted string. The value // is what we need. - PQXX_LIKELY - return std::tuple{juncture::string_value, endpoint}; + [[likely]] return std::tuple{juncture::string_value, endpoint}; } } } @@ -146,9 +145,8 @@ std::pair array_parser::parse_array_step() if (end < std::size(m_input)) { auto next{scan_glyph(end)}; - if (((next - end) == 1) and (m_input[end] == ',')) - PQXX_UNLIKELY - end = next; + if (((next - end) == 1) and (m_input[end] == ',')) [[unlikely]] + end = next; } m_pos = end; @@ -181,7 +179,7 @@ array_parser::specialize_for_encoding(pqxx::internal::encoding_group enc) PQXX_ENCODING_CASE(UHC); PQXX_ENCODING_CASE(UTF8); } - PQXX_UNLIKELY throw pqxx::internal_error{ + [[unlikely]] throw pqxx::internal_error{ pqxx::internal::concat("Unsupported encoding code: ", enc, ".")}; #undef PQXX_ENCODING_CASE diff --git a/src/connection.cxx b/src/connection.cxx index 53397dd05..c95e13c77 100644 --- a/src/connection.cxx +++ b/src/connection.cxx @@ -162,7 +162,7 @@ std::pair pqxx::connection::poll_connect() case PGRES_POLLING_OK: if (not is_open()) throw pqxx::broken_connection{PQerrorMessage(m_conn)}; - PQXX_LIKELY return std::make_pair(false, false); + [[likely]] return std::make_pair(false, false); case PGRES_POLLING_ACTIVE: throw internal_error{ "Nonblocking connection poll returned obsolete 'active' state."}; @@ -477,8 +477,7 @@ pqxx::connection::remove_receiver(pqxx::notification_receiver *T) noexcept if (i == R.second) { - PQXX_UNLIKELY - process_notice(internal::concat( + [[unlikely]] process_notice(internal::concat( "Attempt to remove unknown receiver '", needle.first, "'\n")); } else @@ -529,16 +528,14 @@ void PQXX_COLD pqxx::connection::cancel_query() { std::unique_ptr const cancel{ PQgetCancel(m_conn), wrap_pgfreecancel}; - if (cancel == nullptr) - PQXX_UNLIKELY - throw std::bad_alloc{}; + if (cancel == nullptr) [[unlikely]] + throw std::bad_alloc{}; std::array errbuf{}; auto const err{errbuf.data()}; auto const c{PQcancel(cancel.get(), err, buf_size)}; - if (c == 0) - PQXX_UNLIKELY - throw pqxx::sql_error{std::string{err, std::size(errbuf)}, "[cancel]"}; + if (c == 0) [[unlikely]] + throw pqxx::sql_error{std::string{err, std::size(errbuf)}, "[cancel]"}; } @@ -607,9 +604,8 @@ int pqxx::connection::get_notifs() // Even if somehow we receive notifications during our transaction, don't // deliver them. - if (m_trans != nullptr) - PQXX_UNLIKELY - return 0; + if (m_trans != nullptr) [[unlikely]] + return 0; int notifs = 0; @@ -790,17 +786,16 @@ void pqxx::connection::close() return; try { - if (m_trans) - PQXX_UNLIKELY - process_notice(internal::concat( - "Closing connection while ", - internal::describe_object("transaction"sv, m_trans->name()), - " is still open.\n")); + if (m_trans) [[unlikely]] + process_notice(internal::concat( + "Closing connection while ", + internal::describe_object("transaction"sv, m_trans->name()), + " is still open.\n")); if (not std::empty(m_receivers)) { - PQXX_UNLIKELY - process_notice("Closing connection with outstanding receivers.\n"); + [[unlikely]] process_notice( + "Closing connection with outstanding receivers.\n"); m_receivers.clear(); } @@ -897,7 +892,7 @@ pqxx::connection::read_copy_line() throw internal_error{"table read inexplicably went asynchronous"}; default: // Success, got buffer size. - PQXX_LIKELY + [[likely]] { // Line size includes a trailing zero, which we ignore. auto const text_len{static_cast(line_len) - 1}; @@ -915,12 +910,10 @@ void pqxx::connection::write_copy_line(std::string_view line) static std::string const err_prefix{"Error writing to table: "}; auto const size{check_cast( internal::ssize(line), "Line in stream_to is too long to process."sv)}; - if (PQputCopyData(m_conn, line.data(), size) <= 0) - PQXX_UNLIKELY - throw failure{err_prefix + err_msg()}; - if (PQputCopyData(m_conn, "\n", 1) <= 0) - PQXX_UNLIKELY - throw failure{err_prefix + err_msg()}; + if (PQputCopyData(m_conn, line.data(), size) <= 0) [[unlikely]] + throw failure{err_prefix + err_msg()}; + if (PQputCopyData(m_conn, "\n", 1) <= 0) [[unlikely]] + throw failure{err_prefix + err_msg()}; } @@ -948,9 +941,8 @@ void pqxx::connection::end_copy_write() void pqxx::connection::start_exec(char const query[]) { - if (PQsendQuery(m_conn, query) == 0) - PQXX_UNLIKELY - throw failure{err_msg()}; + if (PQsendQuery(m_conn, query) == 0) [[unlikely]] + throw failure{err_msg()}; } @@ -965,9 +957,8 @@ size_t pqxx::connection::esc_to_buf(std::string_view text, char *buf) const int err{0}; auto const copied{ PQescapeStringConn(m_conn, buf, text.data(), std::size(text), &err)}; - if (err) - PQXX_UNLIKELY - throw argument_error{err_msg()}; + if (err) [[unlikely]] + throw argument_error{err_msg()}; return copied; } @@ -1049,9 +1040,8 @@ std::string pqxx::connection::quote_name(std::string_view identifier) const std::unique_ptr const buf{ PQescapeIdentifier(m_conn, identifier.data(), std::size(identifier)), pqxx::internal::pq::pqfreemem}; - if (buf == nullptr) - PQXX_UNLIKELY - throw failure{err_msg()}; + if (buf == nullptr) [[unlikely]] + throw failure{err_msg()}; return std::string{buf.get()}; } @@ -1080,7 +1070,8 @@ pqxx::connection::esc_like(std::string_view text, char escape_char) const internal::enc_group(encoding_id()), [&out, escape_char](char const *gbegin, char const *gend) { if ((gend - gbegin == 1) and (*gbegin == '_' or *gbegin == '%')) - PQXX_UNLIKELY out.push_back(escape_char); + [[unlikely]] + out.push_back(escape_char); for (; gbegin != gend; ++gbegin) out.push_back(*gbegin); }, @@ -1094,8 +1085,7 @@ int pqxx::connection::await_notification() int notifs = get_notifs(); if (notifs == 0) { - PQXX_LIKELY - internal::wait_fd(socket_of(m_conn), true, false, 10, 0); + [[likely]] internal::wait_fd(socket_of(m_conn), true, false, 10, 0); notifs = get_notifs(); } return notifs; @@ -1108,8 +1098,7 @@ int pqxx::connection::await_notification( int const notifs = get_notifs(); if (notifs == 0) { - PQXX_LIKELY - internal::wait_fd( + [[likely]] internal::wait_fd( socket_of(m_conn), true, false, check_cast(seconds, "Seconds out of range."), check_cast(microseconds, "Microseconds out of range.")); @@ -1141,17 +1130,15 @@ void PQXX_COLD pqxx::connection::set_client_encoding(char const encoding[]) & { case 0: // OK. - PQXX_LIKELY - break; + [[likely]] break; case -1: - PQXX_UNLIKELY + [[unlikely]] if (is_open()) throw failure{"Setting client encoding failed."}; else throw broken_connection{"Lost connection to the database server."}; default: - PQXX_UNLIKELY - throw internal_error{internal::concat( + [[unlikely]] throw internal_error{internal::concat( "Unexpected result from PQsetClientEncoding: ", retval)}; } } @@ -1167,14 +1154,13 @@ int pqxx::connection::encoding_id() const // *before* checking a query result for failure. So, we need to handle // connection failure here and it will apply in lots of places. // TODO: Make pqxx::result::result(...) do all the checking. - PQXX_UNLIKELY + [[unlikely]] if (is_open()) throw failure{"Could not obtain client encoding."}; else throw broken_connection{"Lost connection to the database server."}; } - PQXX_LIKELY - return enc; + [[likely]] return enc; } @@ -1236,15 +1222,13 @@ void pqconninfofree(PQconninfoOption *ptr) std::string pqxx::connection::connection_string() const { - if (m_conn == nullptr) - PQXX_UNLIKELY - throw usage_error{"Can't get connection string: connection is not open."}; + if (m_conn == nullptr) [[unlikely]] + throw usage_error{"Can't get connection string: connection is not open."}; std::unique_ptr const params{ PQconninfo(m_conn), pqconninfofree}; - if (params == nullptr) - PQXX_UNLIKELY - throw std::bad_alloc{}; + if (params == nullptr) [[unlikely]] + throw std::bad_alloc{}; std::string buf; for (std::size_t i{0}; params.get()[i].keyword != nullptr; ++i) diff --git a/src/cursor.cxx b/src/cursor.cxx index 5fac845bb..3e12c0fc5 100644 --- a/src/cursor.cxx +++ b/src/cursor.cxx @@ -247,13 +247,12 @@ pqxx::icursor_iterator &pqxx::icursor_iterator::operator+=(difference_type n) { if (n <= 0) { - PQXX_UNLIKELY + [[unlikely]] if (n == 0) return *this; throw argument_error{"Advancing icursor_iterator by negative offset."}; } - PQXX_LIKELY - m_pos = difference_type( + [[likely]] m_pos = difference_type( pqxx::internal::gate::icursorstream_icursor_iterator{*m_stream}.forward( icursorstream::size_type(n))); m_here.clear(); @@ -267,13 +266,12 @@ pqxx::icursor_iterator::operator=(icursor_iterator const &rhs) noexcept if (&rhs == this) {} else if (rhs.m_stream == m_stream) { - PQXX_UNLIKELY - m_here = rhs.m_here; + [[unlikely]] m_here = rhs.m_here; m_pos = rhs.m_pos; } else { - PQXX_LIKELY + [[likely]] if (m_stream != nullptr) pqxx::internal::gate::icursorstream_icursor_iterator{*m_stream} .remove_iterator(this); diff --git a/src/encodings.cxx b/src/encodings.cxx index e053bf870..f583f644b 100644 --- a/src/encodings.cxx +++ b/src/encodings.cxx @@ -63,8 +63,7 @@ pqxx::internal::encoding_group enc_group(std::string_view encoding_name) case 'B': if (encoding_name == "BIG5"sv) return pqxx::internal::encoding_group::BIG5; - PQXX_UNLIKELY - break; + [[unlikely]] break; case 'E': // C++20: Use string_view::starts_with(). if ((sz >= 6u) and (encoding_name.substr(0, 4) == "EUC_"sv)) @@ -82,15 +81,13 @@ pqxx::internal::encoding_group enc_group(std::string_view encoding_name) if (m.get_name() == subtype) return m.get_group(); } - PQXX_UNLIKELY - break; + [[unlikely]] break; case 'G': if (encoding_name == "GB18030"sv) return pqxx::internal::encoding_group::GB18030; else if (encoding_name == "GBK"sv) return pqxx::internal::encoding_group::GBK; - PQXX_UNLIKELY - break; + [[unlikely]] break; case 'I': // We know iso-8859-X, where 5 <= X < 9. They're all monobyte encodings. // C++20: Use string_view::starts_with(). @@ -100,18 +97,15 @@ pqxx::internal::encoding_group enc_group(std::string_view encoding_name) if (('5' <= subtype) and (subtype < '9')) return pqxx::internal::encoding_group::MONOBYTE; } - PQXX_UNLIKELY - break; + [[unlikely]] break; case 'J': if (encoding_name == "JOHAB"sv) return pqxx::internal::encoding_group::JOHAB; - PQXX_UNLIKELY - break; + [[unlikely]] break; case 'K': if ((encoding_name == "KOI8R"sv) or (encoding_name == "KOI8U"sv)) return pqxx::internal::encoding_group::MONOBYTE; - PQXX_UNLIKELY - break; + [[unlikely]] break; case 'L': // We know LATIN1 through LATIN10. // C++20: Use string_view::starts_with(). @@ -129,13 +123,11 @@ pqxx::internal::encoding_group enc_group(std::string_view encoding_name) return pqxx::internal::encoding_group::MONOBYTE; } } - PQXX_UNLIKELY - break; + [[unlikely]] break; case 'M': if (encoding_name == "MULE_INTERNAL"sv) return pqxx::internal::encoding_group::MULE_INTERNAL; - PQXX_UNLIKELY - break; + [[unlikely]] break; case 'S': if (encoding_name == "SHIFT_JIS_2004"sv) return pqxx::internal::encoding_group::SJIS; @@ -143,15 +135,13 @@ pqxx::internal::encoding_group enc_group(std::string_view encoding_name) return pqxx::internal::encoding_group::SJIS; else if (encoding_name == "SQL_ASCII"sv) return pqxx::internal::encoding_group::MONOBYTE; - PQXX_UNLIKELY - break; + [[unlikely]] break; case 'U': if (encoding_name == "UHC"sv) return pqxx::internal::encoding_group::UHC; else if (encoding_name == "UTF8"sv) return pqxx::internal::encoding_group::UTF8; - PQXX_UNLIKELY - break; + [[unlikely]] break; case 'W': if (encoding_name.substr(0, 3) == "WIN"sv) { @@ -164,12 +154,10 @@ pqxx::internal::encoding_group enc_group(std::string_view encoding_name) if (n == subtype) return pqxx::internal::encoding_group::MONOBYTE; } - PQXX_UNLIKELY - break; - default: PQXX_UNLIKELY break; + [[unlikely]] break; + default: [[unlikely]] break; } - PQXX_UNLIKELY - throw std::invalid_argument{ + [[unlikely]] throw std::invalid_argument{ pqxx::internal::concat("Unrecognized encoding: '", encoding_name, "'.")}; } @@ -194,7 +182,7 @@ PQXX_PURE glyph_scanner_func *get_glyph_scanner(encoding_group enc) switch (enc) { - PQXX_LIKELY CASE_GROUP(MONOBYTE); + [[likely]] CASE_GROUP(MONOBYTE); CASE_GROUP(BIG5); CASE_GROUP(EUC_CN); CASE_GROUP(EUC_JP); @@ -206,9 +194,8 @@ PQXX_PURE glyph_scanner_func *get_glyph_scanner(encoding_group enc) CASE_GROUP(MULE_INTERNAL); CASE_GROUP(SJIS); CASE_GROUP(UHC); - PQXX_LIKELY CASE_GROUP(UTF8); + [[likely]] CASE_GROUP(UTF8); } - PQXX_UNLIKELY throw usage_error{ internal::concat("Unsupported encoding group code ", enc, ".")}; diff --git a/src/pipeline.cxx b/src/pipeline.cxx index 99bea4a5d..199378dcd 100644 --- a/src/pipeline.cxx +++ b/src/pipeline.cxx @@ -238,9 +238,8 @@ bool pqxx::pipeline::obtain_result(bool expect_none) gate.get_result(), pqxx::internal::clear_result}; if (not r) { - if (have_pending() and not expect_none) + if (have_pending() and not expect_none) [[unlikely]] { - PQXX_UNLIKELY set_error_at(m_issuedrange.first->first); m_issuedrange.second = m_issuedrange.first; } @@ -252,18 +251,16 @@ bool pqxx::pipeline::obtain_result(bool expect_none) result const res{pqxx::internal::gate::result_creation::create( r, std::begin(m_queries)->second.query, handler, m_encoding)}; - if (not have_pending()) + if (not have_pending()) [[unlikely]] { - PQXX_UNLIKELY set_error_at(std::begin(m_queries)->first); throw std::logic_error{ "Got more results from pipeline than there were queries."}; } // Must be the result for the oldest pending query. - if (not std::empty(m_issuedrange.first->second.res)) - PQXX_UNLIKELY - internal_error("Multiple results for one query."); + if (not std::empty(m_issuedrange.first->second.res)) [[unlikely]] + internal_error("Multiple results for one query."); m_issuedrange.first->second.res = res; ++m_issuedrange.first; @@ -283,9 +280,9 @@ void pqxx::pipeline::obtain_dummy() gate.get_result(), pqxx::internal::clear_result}; m_dummy_pending = false; - if (not r) - PQXX_UNLIKELY - internal_error("Pipeline got no result from backend when it expected one."); + if (not r) [[unlikely]] + internal_error( + "Pipeline got no result from backend when it expected one."); pqxx::internal::gate::connection_pipeline const pgate{m_trans->conn()}; auto handler{pgate.get_notice_waiters()}; @@ -300,16 +297,13 @@ void pqxx::pipeline::obtain_dummy() } catch (sql_error const &) {} - if (OK) + if (OK) [[likely]] { - PQXX_LIKELY - if (std::size(R) > 1) - PQXX_UNLIKELY - internal_error("Unexpected result for dummy query in pipeline."); - - if (R.at(0).at(0).as() != theDummyValue) - PQXX_UNLIKELY - internal_error("Dummy query in pipeline returned unexpected value."); + if (std::size(R) > 1) [[unlikely]] + internal_error("Unexpected result for dummy query in pipeline."); + + if (R.at(0).at(0).as() != theDummyValue) [[unlikely]] + internal_error("Dummy query in pipeline returned unexpected value."); return; } diff --git a/src/result.cxx b/src/result.cxx index a0dca41be..ad13f207f 100644 --- a/src/result.cxx +++ b/src/result.cxx @@ -63,8 +63,8 @@ pqxx::result::result( bool pqxx::result::operator==(result const &rhs) const noexcept { - if (&rhs == this) - PQXX_UNLIKELY return true; + if (&rhs == this) [[unlikely]] + return true; auto const s{size()}; if (std::size(rhs) != s) return false; @@ -202,7 +202,6 @@ void PQXX_COLD pqxx::result::throw_sql_error( switch (code[0]) { - PQXX_UNLIKELY case '\0': // SQLSTATE is empty. We may have seen this happen in one // circumstance: a client-side socket timeout (while using the @@ -306,9 +305,8 @@ void PQXX_COLD pqxx::result::throw_sql_error( void pqxx::result::check_status(std::string_view desc) const { - if (auto err{status_error()}; not std::empty(err)) + if (auto err{status_error()}; not std::empty(err)) [[unlikely]] { - PQXX_UNLIKELY if (not std::empty(desc)) err = pqxx::internal::concat("Failure during '", desc, "': ", err); throw_sql_error(err, query()); @@ -342,8 +340,7 @@ std::string pqxx::result::status_error() const case PGRES_BAD_RESPONSE: // The server's response was not understood. case PGRES_NONFATAL_ERROR: case PGRES_FATAL_ERROR: - PQXX_UNLIKELY - err = PQresultErrorMessage(m_data.get()); + [[unlikely]] err = PQresultErrorMessage(m_data.get()); break; case PGRES_SINGLE_TUPLE: @@ -457,9 +454,8 @@ pqxx::oid pqxx::result::column_table(row::size_type col_num) const pqxx::row::size_type pqxx::result::table_column(row::size_type col_num) const { auto const n{row::size_type(PQftablecol(m_data.get(), col_num))}; - if (n != 0) - PQXX_LIKELY - return n - 1; + if (n != 0) [[likely]] + return n - 1; // Failed. Now find out why, so we can throw a sensible exception. auto const col_str{to_string(col_num)}; @@ -494,9 +490,8 @@ int pqxx::result::errorposition() const char const *pqxx::result::column_name(pqxx::row::size_type number) const & { auto const n{PQfname(m_data.get(), number)}; - if (n == nullptr) + if (n == nullptr) [[unlikely]] { - PQXX_UNLIKELY if (m_data.get() == nullptr) throw usage_error{"Queried column name on null result."}; throw range_error{internal::concat( diff --git a/src/robusttransaction.cxx b/src/robusttransaction.cxx index e9b900a46..f2726e33b 100644 --- a/src/robusttransaction.cxx +++ b/src/robusttransaction.cxx @@ -56,16 +56,16 @@ constexpr tx_stat parse_status(std::string_view text) noexcept switch (text[0]) { case 'a': - if (text == aborted) - PQXX_LIKELY return tx_aborted; + if (text == aborted) [[likely]] + return tx_aborted; break; case 'c': - if (text == committed) - PQXX_LIKELY return tx_committed; + if (text == committed) [[likely]] + return tx_committed; break; case 'i': - if (text == in_progress) - PQXX_LIKELY return tx_in_progress; + if (text == in_progress) [[likely]] + return tx_in_progress; break; } return tx_unknown; diff --git a/src/strconv.cxx b/src/strconv.cxx index 7fe7fba2d..734d2cc7d 100644 --- a/src/strconv.cxx +++ b/src/strconv.cxx @@ -123,20 +123,19 @@ template inline char *wrap_to_chars(char *begin, char *end, T const &value) { auto res{std::to_chars(begin, end - 1, value)}; - if (res.ec != std::errc()) - PQXX_UNLIKELY - switch (res.ec) - { - case std::errc::value_too_large: - throw pqxx::conversion_overrun{ - "Could not convert " + pqxx::type_name + - " to string: " - "buffer too small (" + - pqxx::to_string(end - begin) + " bytes)."}; - default: - throw pqxx::conversion_error{ - "Could not convert " + pqxx::type_name + " to string."}; - } + if (res.ec != std::errc()) [[unlikely]] + switch (res.ec) + { + case std::errc::value_too_large: + throw pqxx::conversion_overrun{ + "Could not convert " + pqxx::type_name + + " to string: " + "buffer too small (" + + pqxx::to_string(end - begin) + " bytes)."}; + default: + throw pqxx::conversion_error{ + "Could not convert " + pqxx::type_name + " to string."}; + } // No need to check for overrun here: we never even told to_chars about that // last byte in the buffer, so it didn't get used up. *res.ptr++ = '\0'; @@ -294,9 +293,8 @@ template inline TYPE from_string_arithmetic(std::string_view in) TYPE out{}; auto const res{std::from_chars(here, end, out)}; - if (res.ec == std::errc() and res.ptr == end) - PQXX_LIKELY - return out; + if (res.ec == std::errc() and res.ptr == end) [[likely]] + return out; std::string msg; if (res.ec == std::errc()) @@ -404,9 +402,8 @@ inline T PQXX_COLD from_string_awful_float(std::string_view text) ok = true; result = -std::numeric_limits::infinity(); } - else + else [[likely]] { - PQXX_LIKELY if constexpr (have_thread_local) { thread_local dumb_stringstream S; diff --git a/src/stream_from.cxx b/src/stream_from.cxx index d15b11c66..98d7ace7b 100644 --- a/src/stream_from.cxx +++ b/src/stream_from.cxx @@ -61,12 +61,12 @@ pqxx::stream_from::stream_from( from_table_t) : transaction_focus{tx, class_name, table}, m_char_finder{get_finder(tx)} { - if (std::empty(columns)) - PQXX_UNLIKELY - tx.exec(internal::concat("COPY "sv, table, " TO STDOUT"sv)).no_rows(); - else PQXX_LIKELY tx - .exec(internal::concat("COPY "sv, table, "("sv, columns, ") TO STDOUT"sv)) - .no_rows(); + if (std::empty(columns)) [[unlikely]] + tx.exec(internal::concat("COPY "sv, table, " TO STDOUT"sv)).no_rows(); + else [[likely]] + tx.exec( + internal::concat("COPY "sv, table, "("sv, columns, ") TO STDOUT"sv)) + .no_rows(); register_me(); } @@ -138,9 +138,8 @@ pqxx::stream_from::raw_line pqxx::stream_from::get_raw_line() void pqxx::stream_from::close() { - if (not m_finished) + if (not m_finished) [[unlikely]] { - PQXX_UNLIKELY m_finished = true; unregister_me(); } @@ -178,9 +177,8 @@ void pqxx::stream_from::complete() void pqxx::stream_from::parse_line() { - if (m_finished) - PQXX_UNLIKELY - return; + if (m_finished) [[unlikely]] + return; // TODO: Any way to keep current size in a local var, for speed? m_fields.clear(); diff --git a/src/stream_to.cxx b/src/stream_to.cxx index b109a3a62..2af257bc7 100644 --- a/src/stream_to.cxx +++ b/src/stream_to.cxx @@ -50,7 +50,7 @@ char escape_char(char special) case '\\': return '\\'; default: break; } - PQXX_UNLIKELY throw pqxx::internal_error{pqxx::internal::concat( + throw pqxx::internal_error{pqxx::internal::concat( "Stream escaping unexpectedly stopped at '", static_cast(static_cast(special)), "'.")}; } diff --git a/src/time.cxx b/src/time.cxx index cc426a072..103779790 100644 --- a/src/time.cxx +++ b/src/time.cxx @@ -38,7 +38,7 @@ inline char * year_into_buf(char *begin, char *end, std::chrono::year const &value) { int const y{value}; - if (y == int{(std::chrono::year::min)()}) + if (y == int{(std::chrono::year::min)()}) [[unlikely]] { // This is an evil special case: C++ year -32767 translates to 32768 BC, // which is a number we can't fit into a short. At the moment postgres @@ -46,7 +46,6 @@ year_into_buf(char *begin, char *end, std::chrono::year const &value) constexpr int oldest{-32767}; static_assert(int{(std::chrono::year::min)()} == oldest); constexpr auto hardcoded{"32768"sv}; - PQXX_UNLIKELY begin += hardcoded.copy(begin, std::size(hardcoded)); } else @@ -63,9 +62,8 @@ year_into_buf(char *begin, char *end, std::chrono::year const &value) // won't be able to deduce the date format correctly. However on output // it always writes years as at least 4 digits, and we'll do the same. // Dates and times are a dirty, dirty business. - if (absy < thousand) + if (absy < thousand) [[unlikely]] { - PQXX_UNLIKELY *begin++ = '0'; if (absy < hundred) *begin++ = '0'; @@ -190,11 +188,8 @@ char *string_traits::into_buf( begin = month_into_buf(begin, value.month()); *begin++ = '-'; begin = day_into_buf(begin, value.day()); - if (int{value.year()} <= 0) - { - PQXX_UNLIKELY + if (int{value.year()} <= 0) [[unlikely]] begin += s_bc.copy(begin, std::size(s_bc)); - } *begin++ = '\0'; return begin; } @@ -208,9 +203,8 @@ string_traits::from_string(std::string_view text) if (std::size(text) < 9) throw conversion_error{make_parse_error(text)}; bool const is_bc{text.ends_with(s_bc)}; - if (is_bc) - PQXX_UNLIKELY - text = text.substr(0, std::size(text) - std::size(s_bc)); + if (is_bc) [[unlikely]] + text = text.substr(0, std::size(text) - std::size(s_bc)); auto const ymsep{find_year_month_separator(text)}; if ((std::size(text) - ymsep) != 6) throw conversion_error{make_parse_error(text)}; diff --git a/src/transaction_base.cxx b/src/transaction_base.cxx index 242158f46..3972d0a3c 100644 --- a/src/transaction_base.cxx +++ b/src/transaction_base.cxx @@ -60,10 +60,9 @@ pqxx::transaction_base::~transaction_base() { try { - if (not std::empty(m_pending_error)) - PQXX_UNLIKELY - process_notice( - internal::concat("UNPROCESSED ERROR: ", m_pending_error, "\n")); + if (not std::empty(m_pending_error)) [[unlikely]] + process_notice( + internal::concat("UNPROCESSED ERROR: ", m_pending_error, "\n")); if (m_registered) { @@ -353,11 +352,10 @@ void pqxx::transaction_base::close() noexcept if (m_status != status::active) return; - if (m_focus != nullptr) - PQXX_UNLIKELY - m_conn.process_notice(internal::concat( - "Closing ", description(), " with ", m_focus->description(), - " still open.\n")); + if (m_focus != nullptr) [[unlikely]] + m_conn.process_notice(internal::concat( + "Closing ", description(), " with ", m_focus->description(), + " still open.\n")); try { @@ -455,8 +453,7 @@ void pqxx::transaction_base::register_pending_error(zview err) noexcept { try { - PQXX_UNLIKELY - process_notice("UNABLE TO PROCESS ERROR\n"); + [[unlikely]] process_notice("UNABLE TO PROCESS ERROR\n"); // TODO: Make at least an attempt to append a newline. process_notice(e.what()); process_notice("ERROR WAS:\n"); @@ -481,7 +478,6 @@ void pqxx::transaction_base::register_pending_error(std::string &&err) noexcept { try { - PQXX_UNLIKELY process_notice("UNABLE TO PROCESS ERROR\n"); // TODO: Make at least an attempt to append a newline. process_notice(e.what()); diff --git a/src/util.cxx b/src/util.cxx index 48aac1316..7367544ad 100644 --- a/src/util.cxx +++ b/src/util.cxx @@ -81,9 +81,8 @@ void pqxx::internal::check_unique_unregister( void const *old_guest, std::string_view old_class, std::string_view old_name, void const *new_guest, std::string_view new_class, std::string_view new_name) { - if (new_guest != old_guest) + if (new_guest != old_guest) [[unlikely]] { - PQXX_UNLIKELY if (new_guest == nullptr) throw usage_error{concat( "Expected to close ", describe_object(old_class, old_name), @@ -120,12 +119,14 @@ constexpr int ten{10}; /// Translate a hex digit to a nibble. Return -1 if it's not a valid digit. constexpr int nibble(int c) noexcept { - if (c >= '0' and c <= '9') - PQXX_LIKELY - return c - '0'; - else if (c >= 'a' and c <= 'f') return ten + (c - 'a'); - else if (c >= 'A' and c <= 'F') return ten + (c - 'A'); - else return -1; + if (c >= '0' and c <= '9') [[likely]] + return c - '0'; + else if (c >= 'a' and c <= 'f') + return ten + (c - 'a'); + else if (c >= 'A' and c <= 'F') + return ten + (c - 'A'); + else + return -1; } } // namespace From ae9f1eafa4e326fc8411b4d0d1ee3222762e0824 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 01:50:47 +0100 Subject: [PATCH 05/57] Assume support for `std::span`. --- cmake/pqxx_cxx_feature_checks.cmake | 4 --- config-tests/PQXX_HAVE_SPAN.cxx | 11 -------- configitems | 1 - configure | 40 --------------------------- cxx_features.txt | 1 - include/pqxx/blob.hxx | 8 ++---- include/pqxx/config.h.in | 3 -- include/pqxx/connection.hxx | 8 ++---- include/pqxx/internal/conversions.hxx | 8 +----- pqxx_cxx_feature_checks.ac | 10 ------- test/unit/test_blob.cxx | 4 --- test/unit/test_escape.cxx | 4 +-- 12 files changed, 8 insertions(+), 94 deletions(-) delete mode 100644 config-tests/PQXX_HAVE_SPAN.cxx diff --git a/cmake/pqxx_cxx_feature_checks.cmake b/cmake/pqxx_cxx_feature_checks.cmake index d9ac004c1..794c5706f 100644 --- a/cmake/pqxx_cxx_feature_checks.cmake +++ b/cmake/pqxx_cxx_feature_checks.cmake @@ -43,10 +43,6 @@ try_compile( PQXX_HAVE_SOURCE_LOCATION ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_SOURCE_LOCATION.cxx ) -try_compile( - PQXX_HAVE_SPAN ${PROJECT_BINARY_DIR} - SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_SPAN.cxx -) try_compile( PQXX_HAVE_SSIZE ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_SSIZE.cxx diff --git a/config-tests/PQXX_HAVE_SPAN.cxx b/config-tests/PQXX_HAVE_SPAN.cxx deleted file mode 100644 index e733a4a0d..000000000 --- a/config-tests/PQXX_HAVE_SPAN.cxx +++ /dev/null @@ -1,11 +0,0 @@ -// Feature check for 'PQXX_HAVE_SPAN'. -// Generated by generate_cxx_checks.py. -#include -#if !defined(__cpp_lib_span) -# error "No PQXX_HAVE_SPAN: __cpp_lib_span is not set." -#endif -#if !__cpp_lib_span -# error "No PQXX_HAVE_SPAN: __cpp_lib_span is false." -#endif - -int main() {} diff --git a/configitems b/configitems index bbac7c1f2..51916d221 100644 --- a/configitems +++ b/configitems @@ -15,7 +15,6 @@ PQXX_HAVE_PATH public compiler PQXX_HAVE_POLL internal compiler PQXX_HAVE_SLEEP_FOR internal compiler PQXX_HAVE_SOURCE_LOCATION public compiler -PQXX_HAVE_SPAN public compiler PQXX_HAVE_SSIZE public compiler PQXX_HAVE_STRERROR_R public compiler PQXX_HAVE_STRERROR_S public compiler diff --git a/configure b/configure index 57860a64a..6e7a2e03f 100755 --- a/configure +++ b/configure @@ -17738,46 +17738,6 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_SOURCE_LOCATION" >&5 printf "%s\n" "$PQXX_HAVE_SOURCE_LOCATION" >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_SPAN" >&5 -printf %s "checking PQXX_HAVE_SPAN... " >&6; } -PQXX_HAVE_SPAN=yes -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -// Feature check for 'PQXX_HAVE_SPAN'. - -// Generated by generate_cxx_checks.py. - -#include - -#if !defined(__cpp_lib_span) - -# error "No PQXX_HAVE_SPAN: __cpp_lib_span is not set." - -#endif - -#if !__cpp_lib_span - -# error "No PQXX_HAVE_SPAN: __cpp_lib_span is false." - -#endif - - - -int main() {} - - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO" -then : - -printf "%s\n" "#define PQXX_HAVE_SPAN 1" >>confdefs.h - -else $as_nop - PQXX_HAVE_SPAN=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_SPAN" >&5 -printf "%s\n" "$PQXX_HAVE_SPAN" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_SSIZE" >&5 printf %s "checking PQXX_HAVE_SSIZE... " >&6; } PQXX_HAVE_SSIZE=yes diff --git a/cxx_features.txt b/cxx_features.txt index c9986486a..d656c8d8d 100644 --- a/cxx_features.txt +++ b/cxx_features.txt @@ -15,5 +15,4 @@ PQXX_HAVE_CONCEPTS __cpp_concepts PQXX_HAVE_MULTIDIM __cpp_multidimensional_subscript PQXX_HAVE_SOURCE_LOCATION __cpp_lib_source_location -PQXX_HAVE_SPAN __cpp_lib_span PQXX_HAVE_SSIZE __cpp_lib_ssize diff --git a/include/pqxx/blob.hxx b/include/pqxx/blob.hxx index 9a8621dee..c4a776aff 100644 --- a/include/pqxx/blob.hxx +++ b/include/pqxx/blob.hxx @@ -106,7 +106,6 @@ public: */ std::size_t read(bytes &buf, std::size_t size); -#if defined(PQXX_HAVE_SPAN) /// Read up to `std::size(buf)` bytes from the object. /** Retrieves bytes from the blob, at the current position, until `buf` is * full or there are no more bytes to read, whichever comes first. @@ -118,9 +117,8 @@ public: { return buf.subspan(0, raw_read(std::data(buf), std::size(buf))); } -#endif // PQXX_HAVE_SPAN -#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) +#if defined(PQXX_HAVE_CONCEPTS) /// Read up to `std::size(buf)` bytes from the object. /** Retrieves bytes from the blob, at the current position, until `buf` is * full or there are no more bytes to read, whichever comes first. @@ -131,7 +129,7 @@ public: { return {std::data(buf), raw_read(std::data(buf), std::size(buf))}; } -#else // PQXX_HAVE_CONCEPTS && PQXX_HAVE_SPAN +#else // PQXX_HAVE_CONCEPTS /// Read up to `std::size(buf)` bytes from the object. /** @deprecated As libpqxx moves to C++20 as its baseline language version, * this will take and return `std::span`. @@ -149,7 +147,7 @@ public: { return {std::data(buf), raw_read(std::data(buf), std::size(buf))}; } -#endif // PQXX_HAVE_CONCEPTS && PQXX_HAVE_SPAN +#endif // PQXX_HAVE_CONCEPTS #if defined(PQXX_HAVE_CONCEPTS) /// Write `data` to large object, at the current position. diff --git a/include/pqxx/config.h.in b/include/pqxx/config.h.in index 26253f1a3..b37533332 100644 --- a/include/pqxx/config.h.in +++ b/include/pqxx/config.h.in @@ -90,9 +90,6 @@ /* Define if this feature is available. */ #undef PQXX_HAVE_SOURCE_LOCATION -/* Define if this feature is available. */ -#undef PQXX_HAVE_SPAN - /* Define if this feature is available. */ #undef PQXX_HAVE_SSIZE diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index 78b1ebc73..a3b7609d5 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -841,7 +841,6 @@ public: return esc(std::string_view{text}); } -#if defined(PQXX_HAVE_SPAN) /// Escape string for use as SQL string literal, into `buffer`. /** Use this variant when you want to re-use the same buffer across multiple * calls. If that's not the case, or convenience and simplicity are more @@ -866,7 +865,6 @@ public: auto const data{buffer.data()}; return {data, esc_to_buf(text, data)}; } -#endif /// Escape string for use as SQL string literal on this connection. /** @warning This is meant for text strings only. It cannot contain bytes @@ -883,7 +881,7 @@ public: } #endif -#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) +#if defined(PQXX_HAVE_CONCEPTS) /// Escape binary string for use as SQL string literal, into `buffer`. /** Use this variant when you want to re-use the same buffer across multiple * calls. If that's not the case, or convenience and simplicity are more @@ -923,11 +921,9 @@ public: /** You can also just use @ref esc with a binary string. */ [[nodiscard]] std::string esc_raw(bytes_view) const; -#if defined(PQXX_HAVE_SPAN) /// Escape binary string for use as SQL string literal, into `buffer`. /** You can also just use @ref esc with a binary string. */ [[nodiscard]] std::string esc_raw(bytes_view, std::span buffer) const; -#endif #if defined(PQXX_HAVE_CONCEPTS) /// Escape binary string for use as SQL string literal on this connection. @@ -939,7 +935,7 @@ public: } #endif -#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) +#if defined(PQXX_HAVE_CONCEPTS) /// Escape binary string for use as SQL string literal, into `buffer`. template [[nodiscard]] zview esc_raw(DATA const &data, std::span buffer) const diff --git a/include/pqxx/internal/conversions.hxx b/include/pqxx/internal/conversions.hxx index 57bc370e9..a5177fa5e 100644 --- a/include/pqxx/internal/conversions.hxx +++ b/include/pqxx/internal/conversions.hxx @@ -4,11 +4,7 @@ #include #include #include - -#if defined(PQXX_HAVE_SPAN) && __has_include() -# include -#endif - +#include #include #include #include @@ -1181,7 +1177,6 @@ inline constexpr format param_format(std::vector const &) template inline constexpr bool is_sql_array>{true}; -#if defined(PQXX_HAVE_SPAN) && __has_include() template struct nullness> : no_null> {}; @@ -1209,7 +1204,6 @@ inline constexpr format param_format(std::span const &) template inline constexpr bool is_sql_array>{true}; -#endif template diff --git a/pqxx_cxx_feature_checks.ac b/pqxx_cxx_feature_checks.ac index fd91792a3..86f28157c 100644 --- a/pqxx_cxx_feature_checks.ac +++ b/pqxx_cxx_feature_checks.ac @@ -109,16 +109,6 @@ AC_COMPILE_IFELSE( [Define if this feature is available.]), PQXX_HAVE_SOURCE_LOCATION=no) AC_MSG_RESULT($PQXX_HAVE_SOURCE_LOCATION) -AC_MSG_CHECKING([PQXX_HAVE_SPAN]) -PQXX_HAVE_SPAN=yes -AC_COMPILE_IFELSE( - [read_test(PQXX_HAVE_SPAN.cxx)], - AC_DEFINE( - [PQXX_HAVE_SPAN], - 1, - [Define if this feature is available.]), - PQXX_HAVE_SPAN=no) -AC_MSG_RESULT($PQXX_HAVE_SPAN) AC_MSG_CHECKING([PQXX_HAVE_SSIZE]) PQXX_HAVE_SSIZE=yes AC_COMPILE_IFELSE( diff --git a/test/unit/test_blob.cxx b/test/unit/test_blob.cxx index 846ec14ad..fb95767e4 100644 --- a/test/unit/test_blob.cxx +++ b/test/unit/test_blob.cxx @@ -183,7 +183,6 @@ template inline unsigned byte_val(BYTE val) void test_blob_read_span() { -#if defined(PQXX_HAVE_SPAN) pqxx::bytes const data{std::byte{'u'}, std::byte{'v'}, std::byte{'w'}, std::byte{'x'}, std::byte{'y'}, std::byte{'z'}}; @@ -230,7 +229,6 @@ void test_blob_read_span() output2 = b.read(vec_buf); PQXX_CHECK_EQUAL(std::size(output2), 1u, "Weird things happened at EOF."); PQXX_CHECK_EQUAL(byte_val(output2[0]), byte_val('z'), "Bad data at EOF."); -#endif // PQXX_HAVE_SPAN } @@ -286,7 +284,6 @@ void test_blob_write_appends_at_insertion_point() void test_blob_writes_span() { -#if defined(PQXX_HAVE_SPAN) pqxx::connection cx; pqxx::work tx{cx}; constexpr char content[]{"gfbltk"}; @@ -307,7 +304,6 @@ void test_blob_writes_span() byte_val(out[0]), byte_val('f'), "Data did not come back right."); PQXX_CHECK_EQUAL( byte_val(out[2]), byte_val('l'), "Data started right, ended wrong!"); -#endif // PQXX_HAVE_SPAN } diff --git a/test/unit/test_escape.cxx b/test/unit/test_escape.cxx index 956583578..d88570ce4 100644 --- a/test/unit/test_escape.cxx +++ b/test/unit/test_escape.cxx @@ -177,7 +177,7 @@ void test_esc_escapes_into_buffer() void test_esc_accepts_various_types() { -#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) +#if defined(PQXX_HAVE_CONCEPTS) pqxx::connection cx; pqxx::work tx{cx}; @@ -197,7 +197,7 @@ void test_esc_accepts_various_types() void test_binary_esc_checks_buffer_length() { -#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN) +#if defined(PQXX_HAVE_CONCEPTS) pqxx::connection cx; pqxx::work tx{cx}; From 24360212637884ee4374e9a9226594348dbed4e6 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 02:03:58 +0100 Subject: [PATCH 06/57] Assume support for `std::ssize()`. --- cmake/pqxx_cxx_feature_checks.cmake | 4 --- config-tests/PQXX_HAVE_SSIZE.cxx | 11 -------- configitems | 1 - configure | 40 ----------------------------- cxx_features.txt | 1 - include/pqxx/config.h.in | 3 --- include/pqxx/params.hxx | 5 ++-- include/pqxx/range.hxx | 2 +- include/pqxx/util.hxx | 12 --------- pqxx_cxx_feature_checks.ac | 10 -------- src/connection.cxx | 2 +- src/params.cxx | 2 +- src/util.cxx | 2 ++ test/unit/test_array.cxx | 3 +-- 14 files changed, 8 insertions(+), 90 deletions(-) delete mode 100644 config-tests/PQXX_HAVE_SSIZE.cxx diff --git a/cmake/pqxx_cxx_feature_checks.cmake b/cmake/pqxx_cxx_feature_checks.cmake index 794c5706f..bd01142c6 100644 --- a/cmake/pqxx_cxx_feature_checks.cmake +++ b/cmake/pqxx_cxx_feature_checks.cmake @@ -43,10 +43,6 @@ try_compile( PQXX_HAVE_SOURCE_LOCATION ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_SOURCE_LOCATION.cxx ) -try_compile( - PQXX_HAVE_SSIZE ${PROJECT_BINARY_DIR} - SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_SSIZE.cxx -) try_compile( PQXX_HAVE_STRERROR_R ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_STRERROR_R.cxx diff --git a/config-tests/PQXX_HAVE_SSIZE.cxx b/config-tests/PQXX_HAVE_SSIZE.cxx deleted file mode 100644 index 1c95aae54..000000000 --- a/config-tests/PQXX_HAVE_SSIZE.cxx +++ /dev/null @@ -1,11 +0,0 @@ -// Feature check for 'PQXX_HAVE_SSIZE'. -// Generated by generate_cxx_checks.py. -#include -#if !defined(__cpp_lib_ssize) -# error "No PQXX_HAVE_SSIZE: __cpp_lib_ssize is not set." -#endif -#if !__cpp_lib_ssize -# error "No PQXX_HAVE_SSIZE: __cpp_lib_ssize is false." -#endif - -int main() {} diff --git a/configitems b/configitems index 51916d221..e4f653b8a 100644 --- a/configitems +++ b/configitems @@ -15,7 +15,6 @@ PQXX_HAVE_PATH public compiler PQXX_HAVE_POLL internal compiler PQXX_HAVE_SLEEP_FOR internal compiler PQXX_HAVE_SOURCE_LOCATION public compiler -PQXX_HAVE_SSIZE public compiler PQXX_HAVE_STRERROR_R public compiler PQXX_HAVE_STRERROR_S public compiler PQXX_HAVE_THREAD_LOCAL internal compiler diff --git a/configure b/configure index 6e7a2e03f..66a55e2e8 100755 --- a/configure +++ b/configure @@ -17738,46 +17738,6 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_SOURCE_LOCATION" >&5 printf "%s\n" "$PQXX_HAVE_SOURCE_LOCATION" >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_SSIZE" >&5 -printf %s "checking PQXX_HAVE_SSIZE... " >&6; } -PQXX_HAVE_SSIZE=yes -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -// Feature check for 'PQXX_HAVE_SSIZE'. - -// Generated by generate_cxx_checks.py. - -#include - -#if !defined(__cpp_lib_ssize) - -# error "No PQXX_HAVE_SSIZE: __cpp_lib_ssize is not set." - -#endif - -#if !__cpp_lib_ssize - -# error "No PQXX_HAVE_SSIZE: __cpp_lib_ssize is false." - -#endif - - - -int main() {} - - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO" -then : - -printf "%s\n" "#define PQXX_HAVE_SSIZE 1" >>confdefs.h - -else $as_nop - PQXX_HAVE_SSIZE=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_SSIZE" >&5 -printf "%s\n" "$PQXX_HAVE_SSIZE" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_STRERROR_R" >&5 printf %s "checking PQXX_HAVE_STRERROR_R... " >&6; } PQXX_HAVE_STRERROR_R=yes diff --git a/cxx_features.txt b/cxx_features.txt index d656c8d8d..464415362 100644 --- a/cxx_features.txt +++ b/cxx_features.txt @@ -15,4 +15,3 @@ PQXX_HAVE_CONCEPTS __cpp_concepts PQXX_HAVE_MULTIDIM __cpp_multidimensional_subscript PQXX_HAVE_SOURCE_LOCATION __cpp_lib_source_location -PQXX_HAVE_SSIZE __cpp_lib_ssize diff --git a/include/pqxx/config.h.in b/include/pqxx/config.h.in index b37533332..b07152fbc 100644 --- a/include/pqxx/config.h.in +++ b/include/pqxx/config.h.in @@ -90,9 +90,6 @@ /* Define if this feature is available. */ #undef PQXX_HAVE_SOURCE_LOCATION -/* Define if this feature is available. */ -#undef PQXX_HAVE_SSIZE - /* Define if this feature is available. */ #undef PQXX_HAVE_STRERROR_R diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index 78d794a82..b14117974 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -52,16 +52,15 @@ public: // C++20: constexpr. /// Get the number of parameters currently in this `params`. - [[nodiscard]] auto size() const noexcept { return m_params.size(); } + [[nodiscard]] constexpr auto size() const noexcept { return m_params.size(); } - // C++20: Use the vector's ssize() directly and go noexcept+constexpr. /// Get the number of parameters (signed). /** Unlike `size()`, this is not yet `noexcept`. That's because C++17's * `std::vector` does not have a `ssize()` member function. These member * functions are `noexcept`, but `std::size()` and `std::ssize()` are * not. */ - [[nodiscard]] auto ssize() const { return pqxx::internal::ssize(m_params); } + [[nodiscard]] constexpr auto ssize() const { return std::ssize(m_params); } /// Append a null value. void append() &; diff --git a/include/pqxx/range.hxx b/include/pqxx/range.hxx index d689c9b9a..ef5afa8ff 100644 --- a/include/pqxx/range.hxx +++ b/include/pqxx/range.hxx @@ -413,7 +413,7 @@ template struct string_traits> { if (value.empty()) { - if ((end - begin) <= internal::ssize(s_empty)) + if ((end - begin) <= std::ssize(s_empty)) throw conversion_overrun{s_overrun.c_str()}; char *here = begin + s_empty.copy(begin, std::size(s_empty)); *here++ = '\0'; diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index 6fb4126c8..f1b1ce1f8 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -489,18 +489,6 @@ unesc_bin(std::string_view escaped_data, std::byte buffer[]); bytes PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data); -/// Transitional: std::ssize(), or custom implementation if not available. -template auto ssize(T const &c) -{ -#if defined(PQXX_HAVE_SSIZE) - return std::ssize(c); -#else - using signed_t = std::make_signed_t; - return static_cast(std::size(c)); -#endif // PQXX_HAVE_SSIZe -} - - /// Helper for determining a function's parameter types. /** This function has no definition. It's not meant to be actually called. * It's just there for pattern-matching in the compiler, so we can use its diff --git a/pqxx_cxx_feature_checks.ac b/pqxx_cxx_feature_checks.ac index 86f28157c..540d4e4ba 100644 --- a/pqxx_cxx_feature_checks.ac +++ b/pqxx_cxx_feature_checks.ac @@ -109,16 +109,6 @@ AC_COMPILE_IFELSE( [Define if this feature is available.]), PQXX_HAVE_SOURCE_LOCATION=no) AC_MSG_RESULT($PQXX_HAVE_SOURCE_LOCATION) -AC_MSG_CHECKING([PQXX_HAVE_SSIZE]) -PQXX_HAVE_SSIZE=yes -AC_COMPILE_IFELSE( - [read_test(PQXX_HAVE_SSIZE.cxx)], - AC_DEFINE( - [PQXX_HAVE_SSIZE], - 1, - [Define if this feature is available.]), - PQXX_HAVE_SSIZE=no) -AC_MSG_RESULT($PQXX_HAVE_SSIZE) AC_MSG_CHECKING([PQXX_HAVE_STRERROR_R]) PQXX_HAVE_STRERROR_R=yes AC_COMPILE_IFELSE( diff --git a/src/connection.cxx b/src/connection.cxx index c95e13c77..1d2ad8dfc 100644 --- a/src/connection.cxx +++ b/src/connection.cxx @@ -909,7 +909,7 @@ void pqxx::connection::write_copy_line(std::string_view line) { static std::string const err_prefix{"Error writing to table: "}; auto const size{check_cast( - internal::ssize(line), "Line in stream_to is too long to process."sv)}; + std::ssize(line), "Line in stream_to is too long to process."sv)}; if (PQputCopyData(m_conn, line.data(), size) <= 0) [[unlikely]] throw failure{err_prefix + err_msg()}; if (PQputCopyData(m_conn, "\n", 1) <= 0) [[unlikely]] diff --git a/src/params.cxx b/src/params.cxx index ad1e38d4d..68c24583e 100644 --- a/src/params.cxx +++ b/src/params.cxx @@ -110,7 +110,7 @@ pqxx::internal::c_params pqxx::params::make_c_params() const { p.values.push_back(reinterpret_cast(std::data(value))); p.lengths.push_back( - check_cast(internal::ssize(value), s_overflow)); + check_cast(std::ssize(value), s_overflow)); } p.formats.push_back(param_format(value)); diff --git a/src/util.cxx b/src/util.cxx index 7367544ad..1b7cc0735 100644 --- a/src/util.cxx +++ b/src/util.cxx @@ -109,6 +109,8 @@ constexpr std::array hex_digits{ /// Translate a number (must be between 0 and 16 exclusive) to a hex digit. constexpr char hex_digit(int c) noexcept { + PQXX_ASSUME(c >= 0); + PQXX_ASSUME(c < std::ssize(hex_digits)); return hex_digits.at(static_cast(c)); } diff --git a/test/unit/test_array.cxx b/test/unit/test_array.cxx index ac1c47a63..c8861f2ac 100644 --- a/test/unit/test_array.cxx +++ b/test/unit/test_array.cxx @@ -730,8 +730,7 @@ void test_array_iterates_in_row_major_order() PQXX_CHECK_EQUAL(*array.crbegin(), 9, "Bad crbegin()."); PQXX_CHECK_EQUAL(*(array.crend() - 1), 1, "Bad crend()."); PQXX_CHECK_EQUAL(std::size(array), 9u, "Bad array size."); - // C++20: Use std::ssize() instead. - PQXX_CHECK_EQUAL(array.ssize(), 9, "Bad array ssize()."); + PQXX_CHECK_EQUAL(std::ssize(array), 9, "Bad array ssize()."); PQXX_CHECK_EQUAL(array.front(), 1, "Bad front()."); PQXX_CHECK_EQUAL(array.back(), 9, "Bad back()."); } From 975ec3903186daddd6a26820d9cfa5093dcf17e7 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 02:17:32 +0100 Subject: [PATCH 07/57] Assume `` & ``. --- include/pqxx/blob.hxx | 11 ++--------- include/pqxx/connection.hxx | 6 +----- include/pqxx/params.hxx | 1 - include/pqxx/strconv.hxx | 6 +----- include/pqxx/transaction_base.hxx | 3 +-- include/pqxx/types.hxx | 5 +---- 6 files changed, 6 insertions(+), 26 deletions(-) diff --git a/include/pqxx/blob.hxx b/include/pqxx/blob.hxx index c4a776aff..0f65dd0b1 100644 --- a/include/pqxx/blob.hxx +++ b/include/pqxx/blob.hxx @@ -23,15 +23,8 @@ # include #endif -// C++20: Assume support. -#if __has_include() -# include -#endif - -// C++20: Assume support. -#if __has_include() -# include -#endif +#include +#include #include "pqxx/dbtransaction.hxx" diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index a3b7609d5..58c369d3d 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -24,15 +24,11 @@ #include #include #include +#include #include #include #include -// Double-check in order to suppress an overzealous Visual C++ warning (#418). -#if defined(PQXX_HAVE_CONCEPTS) && __has_include() -# include -#endif - #include "pqxx/errorhandler.hxx" #include "pqxx/except.hxx" #include "pqxx/internal/concat.hxx" diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index b14117974..aeb19c75d 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -50,7 +50,6 @@ public: */ void reserve(std::size_t n) &; - // C++20: constexpr. /// Get the number of parameters currently in this `params`. [[nodiscard]] constexpr auto size() const noexcept { return m_params.size(); } diff --git a/include/pqxx/strconv.hxx b/include/pqxx/strconv.hxx index fa61b4d30..40bff6c1b 100644 --- a/include/pqxx/strconv.hxx +++ b/include/pqxx/strconv.hxx @@ -19,15 +19,11 @@ #include #include #include +#include #include #include #include -// C++20: Assume support. -#if __has_include() -# include -#endif - #include "pqxx/except.hxx" #include "pqxx/util.hxx" #include "pqxx/zview.hxx" diff --git a/include/pqxx/transaction_base.hxx b/include/pqxx/transaction_base.hxx index fdda1b4ee..8fedd71f6 100644 --- a/include/pqxx/transaction_base.hxx +++ b/include/pqxx/transaction_base.hxx @@ -1042,9 +1042,8 @@ public: [[deprecated("Read variables using SQL SHOW statements.")]] std::string get_variable(std::string_view); - // C++20: constexpr. /// Transaction name, if you passed one to the constructor; or empty string. - [[nodiscard]] std::string_view name() const & noexcept { return m_name; } + [[nodiscard]] constexpr std::string_view name() const & noexcept { return m_name; } protected: /// Create a transaction (to be called by implementation classes only). diff --git a/include/pqxx/types.hxx b/include/pqxx/types.hxx index a1514be38..9858672e7 100644 --- a/include/pqxx/types.hxx +++ b/include/pqxx/types.hxx @@ -16,10 +16,7 @@ #include #include #include - -#if defined(PQXX_HAVE_CONCEPTS) && __has_include() -# include -#endif +#include namespace pqxx From 549d452d119d0c613938fda7624f0375545530ec Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 02:19:30 +0100 Subject: [PATCH 08/57] Remove ``. --- include/pqxx/internal/header-pre.hxx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/pqxx/internal/header-pre.hxx b/include/pqxx/internal/header-pre.hxx index 0b1eb80e3..35f6df45d 100644 --- a/include/pqxx/internal/header-pre.hxx +++ b/include/pqxx/internal/header-pre.hxx @@ -63,13 +63,6 @@ # define PQXX_CPLUSPLUS __cplusplus #endif -// C++20: No longer needed. -// Enable ISO-646 alternative operaotr representations: "and" instead of "&&" -// etc. on older compilers. C++20 removes this header. -#if PQXX_CPLUSPLUS <= 201703L && __has_include() -# include -#endif - #if defined(PQXX_HAVE_GCC_PURE) /// Declare function "pure": no side effects, only reads globals and its args. # define PQXX_PURE __attribute__((pure)) From 64554e1c654e1afd54aa51dc2f608364a5f0af54 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 02:32:08 +0100 Subject: [PATCH 09/57] Various little C++20 updates. --- include/pqxx/connection.hxx | 1 - include/pqxx/zview.hxx | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index 58c369d3d..d56e7534e 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -819,7 +819,6 @@ public: //@} - // C++20: constexpr. Breaks ABI. /// Suffix unique number to name to make it unique within session context. /** Used internally to generate identifiers for SQL objects (such as cursors * and nested transactions) based on a given human-readable base name. diff --git a/include/pqxx/zview.hxx b/include/pqxx/zview.hxx index a35460e97..b40f95839 100644 --- a/include/pqxx/zview.hxx +++ b/include/pqxx/zview.hxx @@ -64,9 +64,8 @@ public: std::string_view(std::forward(args)...) {} - // C++20: constexpr. /// @warning There's an implicit conversion from `std::string`. - zview(std::string const &str) noexcept : + constexpr zview(std::string const &str) noexcept : std::string_view{str.c_str(), str.size()} {} @@ -157,9 +156,8 @@ inline constexpr char const *as_c_string(pqxx::zview str) noexcept { return str.c_str(); } -// C++20: Make this constexpr. /// Get a raw C string pointer. -inline char const *as_c_string(std::string const &str) noexcept +inline constexpr char const *as_c_string(std::string const &str) noexcept { return str.c_str(); } From c8966deb23361ee718dfff8a4577340c23e247e3 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 02:37:47 +0100 Subject: [PATCH 10/57] Raise baseline C++ version to C++20. --- configure | 6 +++--- configure.ac | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configure b/configure index 66a55e2e8..6e984fed0 100755 --- a/configure +++ b/configure @@ -17061,7 +17061,7 @@ add_compiler_opts() { # It's tempting to use Autoconf Archive's AX_CXX_COMPILE_STDCXX_17 for this, -# but it's 2022 and the C++20 equivalent isn't quite ready for use. +# but it's not getting a lot of maintenance. # Seems simpler and more reliable for the user to arrange for the desired # language versions by setting the appropriate option for their compiler. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sufficient C++ language/library level" >&5 @@ -17070,8 +17070,8 @@ sufficient_cxx=yes cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - #if __cplusplus < 201611L - #error "Need C++17 or better." + #if __cplusplus < 202002L + #error "Need C++20 or better." #endif _ACEOF diff --git a/configure.ac b/configure.ac index 064ab2243..8d105a8c1 100644 --- a/configure.ac +++ b/configure.ac @@ -92,15 +92,15 @@ add_compiler_opts() { # It's tempting to use Autoconf Archive's AX_CXX_COMPILE_STDCXX_17 for this, -# but it's 2022 and the C++20 equivalent isn't quite ready for use. +# but it's not getting a lot of maintenance. # Seems simpler and more reliable for the user to arrange for the desired # language versions by setting the appropriate option for their compiler. AC_MSG_CHECKING([for sufficient C++ language/library level]) sufficient_cxx=yes AC_COMPILE_IFELSE( [AC_LANG_SOURCE([ - #if __cplusplus < 201611L - #error "Need C++17 or better." + #if __cplusplus < 202002L + #error "Need C++20 or better." #endif ])], sufficient_cxx=yes, From 0a98a8e4b8073e16e9781f7b3c43bdc1c3cdfe67 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 02:42:11 +0100 Subject: [PATCH 11/57] Format. --- include/pqxx/params.hxx | 5 ++++- include/pqxx/transaction_base.hxx | 5 ++++- src/params.cxx | 3 +-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index aeb19c75d..ad76cf06b 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -51,7 +51,10 @@ public: void reserve(std::size_t n) &; /// Get the number of parameters currently in this `params`. - [[nodiscard]] constexpr auto size() const noexcept { return m_params.size(); } + [[nodiscard]] constexpr auto size() const noexcept + { + return m_params.size(); + } /// Get the number of parameters (signed). /** Unlike `size()`, this is not yet `noexcept`. That's because C++17's diff --git a/include/pqxx/transaction_base.hxx b/include/pqxx/transaction_base.hxx index 8fedd71f6..5d9599c2f 100644 --- a/include/pqxx/transaction_base.hxx +++ b/include/pqxx/transaction_base.hxx @@ -1043,7 +1043,10 @@ public: std::string get_variable(std::string_view); /// Transaction name, if you passed one to the constructor; or empty string. - [[nodiscard]] constexpr std::string_view name() const & noexcept { return m_name; } + [[nodiscard]] constexpr std::string_view name() const & noexcept + { + return m_name; + } protected: /// Create a transaction (to be called by implementation classes only). diff --git a/src/params.cxx b/src/params.cxx index 68c24583e..9c19de05a 100644 --- a/src/params.cxx +++ b/src/params.cxx @@ -109,8 +109,7 @@ pqxx::internal::c_params pqxx::params::make_c_params() const else { p.values.push_back(reinterpret_cast(std::data(value))); - p.lengths.push_back( - check_cast(std::ssize(value), s_overflow)); + p.lengths.push_back(check_cast(std::ssize(value), s_overflow)); } p.formats.push_back(param_format(value)); From 5c46ebfe3abb480d25994227ff7e875953614ffe Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 02:50:33 +0100 Subject: [PATCH 12/57] Drop some workarounds for missing concepts. --- include/pqxx/connection.hxx | 4 ++-- include/pqxx/params.hxx | 2 +- include/pqxx/stream_to.hxx | 4 ++-- include/pqxx/types.hxx | 43 ------------------------------------- 4 files changed, 5 insertions(+), 48 deletions(-) diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index d56e7534e..6348b2406 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -1001,7 +1001,7 @@ public: * yourself. It's a bit of extra work, but it can in rare cases let you * eliminate some duplicate work in quoting them repeatedly. */ - template + template inline std::string quote_columns(STRINGS const &columns) const; // TODO: Make "into buffer" variant to eliminate a string allocation. @@ -1472,7 +1472,7 @@ template inline std::string connection::quote(T const &t) const } -template +template inline std::string connection::quote_columns(STRINGS const &columns) const { return separated_list( diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index ad76cf06b..54799e911 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -146,7 +146,7 @@ public: } /// Append all elements of `range` as parameters. - template void append_multi(RANGE const &range) & + template void append_multi(RANGE const &range) & { #if defined(PQXX_HAVE_CONCEPTS) if constexpr (std::ranges::sized_range) diff --git a/include/pqxx/stream_to.hxx b/include/pqxx/stream_to.hxx index 6c4685307..79abc5723 100644 --- a/include/pqxx/stream_to.hxx +++ b/include/pqxx/stream_to.hxx @@ -134,7 +134,7 @@ public: * @param path A @ref table_path designating the target table. * @param columns The columns to which the stream should write. */ - template + template static stream_to table(transaction_base &tx, table_path path, COLUMNS const &columns) { @@ -151,7 +151,7 @@ public: * @param path A @ref table_path designating the target table. * @param columns The columns to which the stream should write. */ - template + template static stream_to table(transaction_base &tx, std::string_view path, COLUMNS const &columns) { diff --git a/include/pqxx/types.hxx b/include/pqxx/types.hxx index 9858672e7..03ded3115 100644 --- a/include/pqxx/types.hxx +++ b/include/pqxx/types.hxx @@ -112,49 +112,6 @@ concept potential_binary = #endif // PQXX_HAVE_CONCEPTS -// C++20: Retire these compatibility definitions. -#if defined(PQXX_HAVE_CONCEPTS) - -/// Template argument type for a range. -/** This is a concept, so only available in C++20 or better. In pre-C++20 - * environments it's just an alias for @ref typename. - */ -# define PQXX_RANGE_ARG std::ranges::range - -/// Template argument type for @ref char_string. -/** This is a concept, so only available in C++20 or better. In pre-C++20 - * environments it's just an alias for @ref typename. - */ -# define PQXX_CHAR_STRING_ARG pqxx::char_string - -/// Template argument type for @ref char_strings -/** This is a concept, so only available in C++20 or better. In pre-C++20 - * environments it's just an alias for @ref typename. - */ -# define PQXX_CHAR_STRINGS_ARG pqxx::char_strings - -#else // PQXX_HAVE_CONCEPTS - -/// Template argument type for a range. -/** This is a concept, so only available in C++20 or better. In pre-C++20 - * environments it's just an alias for @ref typename. - */ -# define PQXX_RANGE_ARG typename - -/// Template argument type for @ref char_string. -/** This is a concept, so only available in C++20 or better. In pre-C++20 - * environments it's just an alias for @ref typename. - */ -# define PQXX_CHAR_STRING_ARG typename - -/// Template argument type for @ref char_strings -/** This is a concept, so only available in C++20 or better. In pre-C++20 - * environments it's just an alias for @ref typename. - */ -# define PQXX_CHAR_STRINGS_ARG typename - -#endif // PQXX_HAVE_CONCEPTS - /// Marker for @ref stream_from constructors: "stream from table." /** @deprecated Use @ref stream_from::table() instead. */ From 0e323915b81d5457b5567146f0b86fd07564f4f3 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 02:54:28 +0100 Subject: [PATCH 13/57] More workarounds for no concepts. --- include/pqxx/blob.hxx | 46 ------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/include/pqxx/blob.hxx b/include/pqxx/blob.hxx index 0f65dd0b1..9621fd44e 100644 --- a/include/pqxx/blob.hxx +++ b/include/pqxx/blob.hxx @@ -111,7 +111,6 @@ public: return buf.subspan(0, raw_read(std::data(buf), std::size(buf))); } -#if defined(PQXX_HAVE_CONCEPTS) /// Read up to `std::size(buf)` bytes from the object. /** Retrieves bytes from the blob, at the current position, until `buf` is * full or there are no more bytes to read, whichever comes first. @@ -122,27 +121,7 @@ public: { return {std::data(buf), raw_read(std::data(buf), std::size(buf))}; } -#else // PQXX_HAVE_CONCEPTS - /// Read up to `std::size(buf)` bytes from the object. - /** @deprecated As libpqxx moves to C++20 as its baseline language version, - * this will take and return `std::span`. - * - * Retrieves bytes from the blob, at the current position, until `buf` is - * full (i.e. its current size is reached), or there are no more bytes to - * read, whichever comes first. - * - * This function will not change either the size or the capacity of `buf`, - * only its contents. - * - * Returns the filled portion of `buf`. This may be empty. - */ - template bytes_view read(std::vector &buf) - { - return {std::data(buf), raw_read(std::data(buf), std::size(buf))}; - } -#endif // PQXX_HAVE_CONCEPTS -#if defined(PQXX_HAVE_CONCEPTS) /// Write `data` to large object, at the current position. /** If the writing position is at the end of the object, this will append * `data` to the object's contents and move the writing position so that @@ -166,31 +145,6 @@ public: { raw_write(std::data(data), std::size(data)); } -#else - /// Write `data` large object, at the current position. - /** If the writing position is at the end of the object, this will append - * `data` to the object's contents and move the writing position so that - * it's still at the end. - * - * If the writing position was not at the end, writing will overwrite the - * prior data, but it will not remove data that follows the part where you - * wrote your new data. - * - * @warning This is a big difference from writing to a file. You can - * overwrite some data in a large object, but this does not truncate the - * data that was already there. For example, if the object contained binary - * data "abc", and you write "12" at the starting position, the object will - * contain "12c". - * - * @warning The underlying protocol only supports writes up to 2 GB at a - * time. If you need to write more, try making repeated calls to - * @ref append_from_buf. - */ - template void write(DATA const &data) - { - raw_write(std::data(data), std::size(data)); - } -#endif /// Resize large object to `size` bytes. /** If the blob is more than `size` bytes long, this removes the end so as From 492e9854495f5954e21f0289daaad29e423a313f Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 02:56:54 +0100 Subject: [PATCH 14/57] Bump CircleCI C++ version. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 936eb6e78..956e515a1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,7 +46,7 @@ jobs: --enable-maintainer-mode \ --enable-audit \ --enable-shared --disable-static \ - CXXFLAGS='-O3 -std=c++17' \ + CXXFLAGS='-O3 -std=c++20' \ CXX=clang++ - store_artifacts: path: config.log From 316038c56d52102f62dcf2aba3bd0731be6a34d7 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 03:01:38 +0100 Subject: [PATCH 15/57] Bump C++ version in github workflow. --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1852df5b6..678f363a5 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -48,7 +48,7 @@ jobs: createdb "$(whoami)" # Using clang because currently the gcc build fails with an # address sanitizer (asan) link error. - ./configure --enable-maintainer-mode --enable-audit --disable-static CXXFLAGS='-O1 -std=c++17' CXX=clang++ + ./configure --enable-maintainer-mode --enable-audit --disable-static CXXFLAGS='-O1 -std=c++20' CXX=clang++ make -j4 check || (cat test-suite.log && exit 1 || true) - name: Perform CodeQL Analysis From aa06444a1b201574703a23b05d8d8f202aa005ae Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 12:27:40 +0100 Subject: [PATCH 16/57] Update error message to say C++20 is minimum. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 8d105a8c1..d144a6160 100644 --- a/configure.ac +++ b/configure.ac @@ -108,7 +108,7 @@ AC_COMPILE_IFELSE( AC_MSG_RESULT($sufficient_cxx) if test "$sufficient_cxx" != "yes" then - AC_MSG_ERROR([This libpqxx version needs at least C++17.]) + AC_MSG_ERROR([This libpqxx version needs at least C++20.]) fi From 50f126340148a29d979be7a1c8d32c9874f7b52d Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 12:44:23 +0100 Subject: [PATCH 17/57] Reflect raised baseline in more places. --- CMakeLists.txt | 2 +- README.md | 14 ++------------ configure.ac | 2 +- include/pqxx/params.hxx | 1 + include/pqxx/util.hxx | 15 ++++++--------- requirements.json | 2 +- src/strconv.cxx | 2 +- src/time.cxx | 2 -- tools/test_all.py | 2 +- 9 files changed, 14 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d4ef4133..82fec4622 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ project( ) if(NOT "${CMAKE_CXX_STANDARD}") - set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD 20) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/README.md b/README.md index 49c54f1a2..2d58de3ec 100644 --- a/README.md +++ b/README.md @@ -31,18 +31,8 @@ commits in `master`. For example, to get version 7.1.1: Upgrade notes ------------- -**The 7.x versions require at least C++17.** Make sure your compiler is up to -date. For libpqxx 8.x you will need at least C++20. - -Also, **7.0 makes some breaking changes in rarely used APIs:** - -* There is just a single `connection` class. It connects immediately. -* Custom `connection` classes are no longer supported. -* It's no longer possible to reactivate a connection once it's been closed. -* The API for defining string conversions has changed. - -If you're defining your own type conversions, **7.1 requires one additional -field in your `nullness` traits.** +**The 8.x versions require at least C++20.** Make sure your compiler is up to +date. Building libpqxx diff --git a/configure.ac b/configure.ac index d144a6160..654ce3b6f 100644 --- a/configure.ac +++ b/configure.ac @@ -91,7 +91,7 @@ add_compiler_opts() { } -# It's tempting to use Autoconf Archive's AX_CXX_COMPILE_STDCXX_17 for this, +# It's tempting to use Autoconf Archive's AX_CXX_COMPILE_STDCXX_* for this, # but it's not getting a lot of maintenance. # Seems simpler and more reliable for the user to arrange for the desired # language versions by setting the appropriate option for their compiler. diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index 54799e911..732176469 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -56,6 +56,7 @@ public: return m_params.size(); } + // C++20: noexcept. /// Get the number of parameters (signed). /** Unlike `size()`, this is not yet `noexcept`. That's because C++17's * `std::vector` does not have a `ssize()` member function. These member diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index f1b1ce1f8..57ac93225 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -328,20 +328,17 @@ using bytes_view = std::conditional< /// Cast binary data to a type that libpqxx will recognise as binary. /** There are many different formats for storing binary data in memory. You * may have yours as a `std::string`, or a `std::vector`, or one of - * many other types. + * many other types. In libpqxx we commend a container of `std::byte`. * - * But for libpqxx to recognise your data as binary, it needs to be a - * `pqxx::bytes`, or a `pqxx::bytes_view`; or in C++20 or better, any - * contiguous block of `std::byte`. + * For libpqxx to recognise your data as binary, it needs to be a + * `pqxx::bytes`, or a `pqxx::bytes_view`; but any contiguous block of + * `std::byte` should do. * * Use `binary_cast` as a convenience helper to cast your data as a * `pqxx::bytes_view`. * - * @warning There are two things you should be aware of! First, the data must - * be contiguous in memory. In C++20 the compiler will enforce this, but in - * C++17 it's your own problem. Second, you must keep the object where you - * store the actual data alive for as long as you might use this function's - * return value. + * @warning You must keep the storage holding the actual data alive for as + * long as you might use this function's return value. */ template bytes_view binary_cast(TYPE const &data) diff --git a/requirements.json b/requirements.json index e4373be4c..0e2d0531c 100644 --- a/requirements.json +++ b/requirements.json @@ -1,6 +1,6 @@ { "description": "Minimum versions needed of various things.", - "c++": "17", + "c++": "20", "libpq": "12.0", "postgresql": "12.0", "gcc": "9", diff --git a/src/strconv.cxx b/src/strconv.cxx index 734d2cc7d..0a491f494 100644 --- a/src/strconv.cxx +++ b/src/strconv.cxx @@ -96,7 +96,7 @@ template constexpr inline char *bottom_to_buf(char *end) // any modern-day system I can think of, a signed type's bottom value // has no positive equivalent. Luckily the C++ standards committee can't // think of any exceptions either, so it's the required representation as - // of C++20. We'll assume it right now, while still on C++17. + // of C++20. static_assert(-(bottom + 1) == top); // The unsigned version of T does have the unsigned version of bottom. diff --git a/src/time.cxx b/src/time.cxx index 103779790..e216c1ade 100644 --- a/src/time.cxx +++ b/src/time.cxx @@ -10,8 +10,6 @@ #include "pqxx/internal/header-post.hxx" -// std::chrono::year_month_day is C++20, so let's worry a bit less about C++17 -// compatibility in this file. #if defined(PQXX_HAVE_YEAR_MONTH_DAY) namespace { diff --git a/tools/test_all.py b/tools/test_all.py index 9d6c02f44..9881fae69 100755 --- a/tools/test_all.py +++ b/tools/test_all.py @@ -55,7 +55,7 @@ CLANG = [f'clang++-{ver}' for ver in CLANG_VERSIONS] CXX = GCC + CLANG -DIALECTS = ['17', '20', '2b'] +DIALECTS = ['20', '23'] STDLIB = ( '', From 3f2aacad33ce3c823d183ae1576444d82cc37e43 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 14:58:05 +0100 Subject: [PATCH 18/57] Regenerate `configure`. --- config/Makefile.in | 2 +- configure | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/Makefile.in b/config/Makefile.in index 51586bda2..9b1454986 100644 --- a/config/Makefile.in +++ b/config/Makefile.in @@ -124,7 +124,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/configure b/configure index 6e984fed0..2e7b96cd1 100755 --- a/configure +++ b/configure @@ -17060,7 +17060,7 @@ add_compiler_opts() { } -# It's tempting to use Autoconf Archive's AX_CXX_COMPILE_STDCXX_17 for this, +# It's tempting to use Autoconf Archive's AX_CXX_COMPILE_STDCXX_* for this, # but it's not getting a lot of maintenance. # Seems simpler and more reliable for the user to arrange for the desired # language versions by setting the appropriate option for their compiler. @@ -17086,7 +17086,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf "%s\n" "$sufficient_cxx" >&6; } if test "$sufficient_cxx" != "yes" then - as_fn_error $? "This libpqxx version needs at least C++17." "$LINENO" 5 + as_fn_error $? "This libpqxx version needs at least C++20." "$LINENO" 5 fi From a5fe9c5ff78fcaf442b243c0e9cb0ecd86be7527 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 15:33:39 +0100 Subject: [PATCH 19/57] Retire `binarystring`. This type has been deprecated since 7.2.0, more than 4 years ago. --- NEWS | 4 +- config/Makefile.in | 2 +- include/CMakeLists.txt | 1 - include/Makefile.am | 1 - include/Makefile.in | 1 - include/pqxx/binarystring | 6 - include/pqxx/binarystring.hxx | 235 ------------------ include/pqxx/connection.hxx | 4 +- .../pqxx/internal/statement_parameters.hxx | 1 - include/pqxx/params.hxx | 6 - include/pqxx/pqxx | 1 - include/pqxx/transaction_base.hxx | 6 - include/pqxx/types.hxx | 1 - src/Makefile.am | 1 - src/Makefile.in | 13 +- src/binarystring.cxx | 110 -------- src/connection.cxx | 7 - src/params.cxx | 6 - test/Makefile.am | 1 - test/Makefile.in | 18 +- test/unit/test_binarystring.cxx | 216 ---------------- test/unit/test_prepared_statement.cxx | 10 - 22 files changed, 14 insertions(+), 637 deletions(-) delete mode 100644 include/pqxx/binarystring delete mode 100644 include/pqxx/binarystring.hxx delete mode 100644 src/binarystring.cxx delete mode 100644 test/unit/test_binarystring.cxx diff --git a/NEWS b/NEWS index 8d5c4062e..3588f21c1 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ 8.0.0 - C++20 is now the oldest C++ version that libpqxx supports. - - Compiler must support integral conversions in `charconv`. + - Assume compiler supports integral conversions in `charconv`. + - Assume compiler supports spans, ranges, and `cmp_less` etc. + - Retired `binarystring` and its headers. 7.10.1 - Fix string conversion buffer budget for arrays containing nulls. (#921) - Remove `-fanalyzer` option again; gcc is still broken. diff --git a/config/Makefile.in b/config/Makefile.in index 9b1454986..51586bda2 100644 --- a/config/Makefile.in +++ b/config/Makefile.in @@ -124,7 +124,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 depcomp install-sh ltmain.sh missing mkinstalldirs + config.sub install-sh ltmain.sh missing mkinstalldirs DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 263dd2e9e..6a1746150 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -19,7 +19,6 @@ install( PATTERN *.hxx # TODO: Is there any way to do this with CMake's globbing? PATTERN array - PATTERN binarystring PATTERN blob PATTERN composite PATTERN connection diff --git a/include/Makefile.am b/include/Makefile.am index 5ac0d968d..307006b57 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,7 +2,6 @@ SUBDIRS = pqxx nobase_include_HEADERS= pqxx/pqxx \ pqxx/array pqxx/array.hxx \ - pqxx/binarystring pqxx/binarystring.hxx \ pqxx/blob pqxx/blob.hxx \ pqxx/composite pqxx/composite.hxx \ pqxx/connection pqxx/connection.hxx \ diff --git a/include/Makefile.in b/include/Makefile.in index beced7222..788aef9ab 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -350,7 +350,6 @@ with_postgres_lib = @with_postgres_lib@ SUBDIRS = pqxx nobase_include_HEADERS = pqxx/pqxx \ pqxx/array pqxx/array.hxx \ - pqxx/binarystring pqxx/binarystring.hxx \ pqxx/blob pqxx/blob.hxx \ pqxx/composite pqxx/composite.hxx \ pqxx/connection pqxx/connection.hxx \ diff --git a/include/pqxx/binarystring b/include/pqxx/binarystring deleted file mode 100644 index 77551d9f7..000000000 --- a/include/pqxx/binarystring +++ /dev/null @@ -1,6 +0,0 @@ -/** BYTEA (binary string) conversions. - */ -// Actual definitions in .hxx file so editors and such recognize file type. -#include "pqxx/internal/header-pre.hxx" -#include "pqxx/binarystring.hxx" -#include "pqxx/internal/header-post.hxx" diff --git a/include/pqxx/binarystring.hxx b/include/pqxx/binarystring.hxx deleted file mode 100644 index fe113a42d..000000000 --- a/include/pqxx/binarystring.hxx +++ /dev/null @@ -1,235 +0,0 @@ -/* Deprecated representation for raw, binary data. - * - * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/binarystring instead. - * - * Copyright (c) 2000-2025, Jeroen T. Vermeulen. - * - * See COPYING for copyright license. If you did not receive a file called - * COPYING with this source code, please notify the distributor of this - * mistake, or contact the author. - */ -#ifndef PQXX_H_BINARYSTRING -#define PQXX_H_BINARYSTRING - -#if !defined(PQXX_HEADER_PRE) -# error "Include libpqxx headers as , not ." -#endif - -#include -#include -#include - -#include "pqxx/result.hxx" -#include "pqxx/strconv.hxx" - -namespace pqxx -{ -class binarystring; -template<> struct string_traits; - - -/// Binary data corresponding to PostgreSQL's "BYTEA" binary-string type. -/** @ingroup escaping-functions - * @deprecated Use @c bytes and @c bytes_view for binary data. In C++20 or - * better, any @c contiguous_range of @c std::byte will do. - * - * This class represents a binary string as stored in a field of type @c bytea. - * - * Internally a binarystring is zero-terminated, but it may also contain null - * bytes, they're just like any other byte value. So don't assume that it's - * safe to treat the contents as a C-style string. - * - * The binarystring retains its value even if the result it was obtained from - * is destroyed, but it cannot be copied or assigned. - * - * \relatesalso transaction_base::quote_raw - * - * To include a @c binarystring value in an SQL query, escape and quote it - * using the transaction's @c quote_raw function. - * - * @warning This class is implemented as a reference-counting smart pointer. - * Copying, swapping, and destroying binarystring objects that refer to the - * same underlying data block is not thread-safe. If you wish to pass - * binarystrings around between threads, make sure that each of these - * operations is protected against concurrency with similar operations on the - * same object, or other objects pointing to the same data block. - */ -class PQXX_LIBEXPORT binarystring -{ -public: - using char_type = unsigned char; - using value_type = char_type; - using size_type = std::size_t; - using difference_type = long; - using const_reference = value_type const &; - using const_pointer = value_type const *; - using const_iterator = const_pointer; - using const_reverse_iterator = std::reverse_iterator; - - [[deprecated("Use std::byte for binary data.")]] binarystring( - binarystring const &) = default; - - /// Read and unescape bytea field. - /** The field will be zero-terminated, even if the original bytea field - * isn't. - * @param F the field to read; must be a bytea field - */ - [[deprecated("Use std::byte for binary data.")]] explicit binarystring( - field const &); - - /// Copy binary data from std::string_view on binary data. - /** This is inefficient in that it copies the data to a buffer allocated on - * the heap. - */ - [[deprecated("Use std::byte for binary data.")]] explicit binarystring( - std::string_view); - - /// Copy binary data of given length straight out of memory. - [[deprecated("Use std::byte for binary data.")]] binarystring( - void const *, std::size_t); - - /// Efficiently wrap a buffer of binary data in a @c binarystring. - [[deprecated("Use std::byte for binary data.")]] binarystring( - std::shared_ptr ptr, size_type size) : - m_buf{std::move(ptr)}, m_size{size} - {} - - /// Size of converted string in bytes. - [[nodiscard]] size_type size() const noexcept { return m_size; } - /// Size of converted string in bytes. - [[nodiscard]] size_type length() const noexcept { return size(); } - [[nodiscard]] bool empty() const noexcept { return size() == 0; } - - [[nodiscard]] const_iterator begin() const noexcept { return data(); } - [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); } - [[nodiscard]] const_iterator end() const noexcept { return data() + m_size; } - [[nodiscard]] const_iterator cend() const noexcept { return end(); } - - [[nodiscard]] const_reference front() const noexcept { return *begin(); } - [[nodiscard]] const_reference back() const noexcept - { - return *(data() + m_size - 1); - } - - [[nodiscard]] const_reverse_iterator rbegin() const - { - return const_reverse_iterator{end()}; - } - [[nodiscard]] const_reverse_iterator crbegin() const { return rbegin(); } - [[nodiscard]] const_reverse_iterator rend() const - { - return const_reverse_iterator{begin()}; - } - [[nodiscard]] const_reverse_iterator crend() const { return rend(); } - - /// Unescaped field contents. - [[nodiscard]] value_type const *data() const noexcept { return m_buf.get(); } - - [[nodiscard]] const_reference operator[](size_type i) const noexcept - { - return data()[i]; - } - - [[nodiscard]] PQXX_PURE bool operator==(binarystring const &) const noexcept; - [[nodiscard]] bool operator!=(binarystring const &rhs) const noexcept - { - return not operator==(rhs); - } - - binarystring &operator=(binarystring const &); - - /// Index contained string, checking for valid index. - const_reference at(size_type) const; - - /// Swap contents with other binarystring. - void swap(binarystring &); - - /// Raw character buffer (no terminating zero is added). - /** @warning No terminating zero is added! If the binary data did not end in - * a null character, you will not find one here. - */ - [[nodiscard]] char const *get() const noexcept - { - return reinterpret_cast(m_buf.get()); - } - - /// Read contents as a std::string_view. - [[nodiscard]] std::string_view view() const noexcept - { - return std::string_view(get(), size()); - } - - /// Read as regular C++ string (may include null characters). - /** This creates and returns a new string object. Don't call this - * repeatedly; retrieve your string once and keep it in a local variable. - * Also, do not expect to be able to compare the string's address to that of - * an earlier invocation. - */ - [[nodiscard]] std::string str() const; - - /// Access data as a pointer to @c std::byte. - [[nodiscard]] std::byte const *bytes() const - { - return reinterpret_cast(get()); - } - - /// Read data as a @c bytes_view. - [[nodiscard]] pqxx::bytes_view bytes_view() const - { - return pqxx::bytes_view{bytes(), size()}; - } - -private: - std::shared_ptr m_buf; - size_type m_size{0}; -}; - - -template<> struct nullness : no_null -{}; - - -/// String conversion traits for @c binarystring. -/** Defines the conversions between a @c binarystring and its PostgreSQL - * textual format, for communication with the database. - * - * These conversions rely on the "hex" format which was introduced in - * PostgreSQL 9.0. Both your libpq and the server must be recent enough to - * speak this format. - */ -template<> struct string_traits -{ - static std::size_t size_buffer(binarystring const &value) noexcept - { - return internal::size_esc_bin(std::size(value)); - } - - static zview to_buf(char *begin, char *end, binarystring const &value) - { - return generic_to_buf(begin, end, value); - } - - static char *into_buf(char *begin, char *end, binarystring const &value) - { - auto const budget{size_buffer(value)}; - if (std::cmp_less(end - begin, budget)) - throw conversion_overrun{ - "Not enough buffer space to escape binary data."}; - std::string_view text{value.view()}; - internal::esc_bin(binary_cast(text), begin); - return begin + budget; - } - - static binarystring from_string(std::string_view text) - { - auto const size{pqxx::internal::size_unesc_bin(std::size(text))}; - std::shared_ptr buf{ - new unsigned char[size], [](unsigned char const *x) { delete[] x; }}; - pqxx::internal::unesc_bin(text, reinterpret_cast(buf.get())); -#include "pqxx/internal/ignore-deprecated-pre.hxx" - return binarystring{std::move(buf), size}; -#include "pqxx/internal/ignore-deprecated-post.hxx" - } -}; -} // namespace pqxx -#endif diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index 6348b2406..ed8a884cc 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -33,6 +33,7 @@ #include "pqxx/except.hxx" #include "pqxx/internal/concat.hxx" #include "pqxx/params.hxx" +#include "pqxx/result.hxx" #include "pqxx/separated_list.hxx" #include "pqxx/strconv.hxx" #include "pqxx/types.hxx" @@ -1012,9 +1013,6 @@ public: template [[nodiscard]] inline std::string quote(T const &t) const; - [[deprecated("Use std::byte for binary data.")]] std::string - quote(binarystring const &) const; - // TODO: Make "into buffer" variant to eliminate a string allocation. /// Escape and quote binary data for use as a BYTEA value in SQL statement. [[nodiscard]] std::string quote(bytes_view bytes) const; diff --git a/include/pqxx/internal/statement_parameters.hxx b/include/pqxx/internal/statement_parameters.hxx index d002b6b17..3a44bb774 100644 --- a/include/pqxx/internal/statement_parameters.hxx +++ b/include/pqxx/internal/statement_parameters.hxx @@ -18,7 +18,6 @@ #include #include -#include "pqxx/binarystring.hxx" #include "pqxx/strconv.hxx" #include "pqxx/util.hxx" diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index 732176469..f28d43c6c 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -109,12 +109,6 @@ public: /// Append a non-null binary parameter. void append(bytes &&) &; - /// @deprecated Append binarystring parameter. - /** The binarystring must stay valid for as long as the `params` remains - * active. - */ - void append(binarystring const &value) &; - /// Append all parameters from value. template void append(pqxx::internal::dynamic_params const &value) & diff --git a/include/pqxx/pqxx b/include/pqxx/pqxx index c4110d837..8ed5cfd97 100644 --- a/include/pqxx/pqxx +++ b/include/pqxx/pqxx @@ -2,7 +2,6 @@ #include "pqxx/internal/header-pre.hxx" #include "pqxx/array.hxx" -#include "pqxx/binarystring.hxx" #include "pqxx/blob.hxx" #include "pqxx/connection.hxx" #include "pqxx/cursor.hxx" diff --git a/include/pqxx/transaction_base.hxx b/include/pqxx/transaction_base.hxx index 5d9599c2f..312e78eed 100644 --- a/include/pqxx/transaction_base.hxx +++ b/include/pqxx/transaction_base.hxx @@ -259,12 +259,6 @@ public: return conn().quote(t); } - [[deprecated("Use bytes instead of binarystring.")]] std::string - quote(binarystring const &t) const - { - return conn().quote(t.bytes_view()); - } - /// Binary-escape and quote a binary string for use as an SQL constant. [[deprecated("Use quote(pqxx::bytes_view).")]] std::string quote_raw(unsigned char const bin[], std::size_t len) const diff --git a/include/pqxx/types.hxx b/include/pqxx/types.hxx index 03ded3115..a0f1f7d5e 100644 --- a/include/pqxx/types.hxx +++ b/include/pqxx/types.hxx @@ -42,7 +42,6 @@ using large_object_size_type = int64_t; // Forward declarations, to help break compilation dependencies. // These won't necessarily include all classes in libpqxx. -class binarystring; class connection; class const_result_iterator; class const_reverse_result_iterator; diff --git a/src/Makefile.am b/src/Makefile.am index dfd520941..f41224fa2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,6 @@ lib_LTLIBRARIES = libpqxx.la libpqxx_la_SOURCES = \ array.cxx \ - binarystring.cxx \ blob.cxx \ connection.cxx \ cursor.cxx \ diff --git a/src/Makefile.in b/src/Makefile.in index 49dad8007..b79c9b007 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -134,9 +134,9 @@ am__uninstall_files_from_dir = { \ am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) libpqxx_la_LIBADD = -am_libpqxx_la_OBJECTS = array.lo binarystring.lo blob.lo connection.lo \ - cursor.lo encodings.lo errorhandler.lo except.lo field.lo \ - largeobject.lo notification.lo params.lo pipeline.lo result.lo \ +am_libpqxx_la_OBJECTS = array.lo blob.lo connection.lo cursor.lo \ + encodings.lo errorhandler.lo except.lo field.lo largeobject.lo \ + notification.lo params.lo pipeline.lo result.lo \ robusttransaction.lo sql_cursor.lo strconv.lo stream_from.lo \ stream_to.lo subtransaction.lo time.lo transaction.lo \ transaction_base.lo row.lo util.lo version.lo wait.lo @@ -162,8 +162,7 @@ am__v_at_0 = @ am__v_at_1 = depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__maybe_remake_depfiles = depfiles -am__depfiles_remade = ./$(DEPDIR)/array.Plo \ - ./$(DEPDIR)/binarystring.Plo ./$(DEPDIR)/blob.Plo \ +am__depfiles_remade = ./$(DEPDIR)/array.Plo ./$(DEPDIR)/blob.Plo \ ./$(DEPDIR)/connection.Plo ./$(DEPDIR)/cursor.Plo \ ./$(DEPDIR)/encodings.Plo ./$(DEPDIR)/errorhandler.Plo \ ./$(DEPDIR)/except.Plo ./$(DEPDIR)/field.Plo \ @@ -357,7 +356,6 @@ with_postgres_lib = @with_postgres_lib@ lib_LTLIBRARIES = libpqxx.la libpqxx_la_SOURCES = \ array.cxx \ - binarystring.cxx \ blob.cxx \ connection.cxx \ cursor.cxx \ @@ -476,7 +474,6 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/binarystring.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blob.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cursor.Plo@am__quote@ # am--include-marker @@ -668,7 +665,6 @@ clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ distclean: distclean-am -rm -f ./$(DEPDIR)/array.Plo - -rm -f ./$(DEPDIR)/binarystring.Plo -rm -f ./$(DEPDIR)/blob.Plo -rm -f ./$(DEPDIR)/connection.Plo -rm -f ./$(DEPDIR)/cursor.Plo @@ -740,7 +736,6 @@ installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/array.Plo - -rm -f ./$(DEPDIR)/binarystring.Plo -rm -f ./$(DEPDIR)/blob.Plo -rm -f ./$(DEPDIR)/connection.Plo -rm -f ./$(DEPDIR)/cursor.Plo diff --git a/src/binarystring.cxx b/src/binarystring.cxx deleted file mode 100644 index 1814b4677..000000000 --- a/src/binarystring.cxx +++ /dev/null @@ -1,110 +0,0 @@ -/** Implementation of bytea (binary string) conversions. - * - * Copyright (c) 2000-2025, Jeroen T. Vermeulen. - * - * See COPYING for copyright license. If you did not receive a file called - * COPYING with this source code, please notify the distributor of this - * mistake, or contact the author. - */ -#include "pqxx-source.hxx" - -#include -#include -#include -#include -#include -#include - -extern "C" -{ -#include -} - -#include "pqxx/internal/header-pre.hxx" - -#include "pqxx/binarystring.hxx" -#include "pqxx/field.hxx" -#include "pqxx/strconv.hxx" - -#include "pqxx/internal/header-post.hxx" - - -namespace -{ -/// Copy data to a heap-allocated buffer. -std::shared_ptr - PQXX_COLD copy_to_buffer(void const *data, std::size_t len) -{ - std::shared_ptr ptr{ - // NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc) - static_cast(malloc(len + 1)), std::free}; - if (not ptr) - throw std::bad_alloc{}; - ptr.get()[len] = '\0'; - std::memcpy(ptr.get(), data, len); - return ptr; -} -} // namespace - - -PQXX_COLD pqxx::binarystring::binarystring(field const &F) -{ - unsigned char const *data{ - reinterpret_cast(F.c_str())}; - m_buf = std::shared_ptr{ - PQunescapeBytea(data, &m_size), pqxx::internal::pq::pqfreemem}; - if (m_buf == nullptr) - throw std::bad_alloc{}; -} - - -pqxx::binarystring::binarystring(std::string_view s) : - m_buf{copy_to_buffer(std::data(s), std::size(s))}, m_size{std::size(s)} -{} - - -pqxx::binarystring::binarystring(void const *binary_data, std::size_t len) : - m_buf{copy_to_buffer(binary_data, len)}, m_size{len} -{} - - -bool pqxx::binarystring::operator==(binarystring const &rhs) const noexcept -{ - return (std::size(rhs) == size()) and - (std::memcmp(data(), std::data(rhs), size()) == 0); -} - - -pqxx::binarystring & -pqxx::binarystring::operator=(binarystring const &rhs) = default; - -PQXX_COLD pqxx::binarystring::const_reference -pqxx::binarystring::at(size_type n) const -{ - if (n >= m_size) - { - if (m_size == 0) - throw std::out_of_range{"Accessing empty binarystring"}; - throw std::out_of_range{ - "binarystring index out of range: " + to_string(n) + - " (should be below " + to_string(m_size) + ")"}; - } - return data()[n]; -} - - -PQXX_COLD void pqxx::binarystring::swap(binarystring &rhs) -{ - m_buf.swap(rhs.m_buf); - - // This part very obviously can't go wrong, so do it last - auto const s{m_size}; - m_size = rhs.m_size; - rhs.m_size = s; -} - - -std::string pqxx::binarystring::str() const -{ - return std::string{get(), m_size}; -} diff --git a/src/connection.cxx b/src/connection.cxx index 1d2ad8dfc..c1a36424f 100644 --- a/src/connection.cxx +++ b/src/connection.cxx @@ -46,7 +46,6 @@ extern "C" #include "pqxx/internal/header-pre.hxx" -#include "pqxx/binarystring.hxx" #include "pqxx/internal/wait.hxx" #include "pqxx/nontransaction.hxx" #include "pqxx/notification.hxx" @@ -1023,12 +1022,6 @@ std::string pqxx::connection::quote_raw(bytes_view bytes) const } -std::string PQXX_COLD pqxx::connection::quote(binarystring const &b) const -{ - return quote(b.bytes_view()); -} - - std::string pqxx::connection::quote(bytes_view b) const { return internal::concat("'", esc_raw(b), "'::bytea"); diff --git a/src/params.cxx b/src/params.cxx index 9c19de05a..6ba77d887 100644 --- a/src/params.cxx +++ b/src/params.cxx @@ -78,12 +78,6 @@ void pqxx::params::append(bytes &&value) & } -void PQXX_COLD pqxx::params::append(binarystring const &value) & -{ - m_params.emplace_back(value.bytes_view()); -} - - void pqxx::params::append(params &&value) & { this->reserve(std::size(value.m_params) + std::size(this->m_params)); diff --git a/test/Makefile.am b/test/Makefile.am index c6a150aa4..b00413d6e 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -79,7 +79,6 @@ runner_SOURCES = \ test89.cxx \ test90.cxx \ unit/test_array.cxx \ - unit/test_binarystring.cxx \ unit/test_blob.cxx \ unit/test_cancel_query.cxx \ unit/test_column.cxx \ diff --git a/test/Makefile.in b/test/Makefile.in index c73c756c2..2f8d43afd 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -139,10 +139,10 @@ am_runner_OBJECTS = test00.$(OBJEXT) test01.$(OBJEXT) test02.$(OBJEXT) \ test78.$(OBJEXT) test79.$(OBJEXT) test82.$(OBJEXT) \ test84.$(OBJEXT) test87.$(OBJEXT) test88.$(OBJEXT) \ test89.$(OBJEXT) test90.$(OBJEXT) unit/test_array.$(OBJEXT) \ - unit/test_binarystring.$(OBJEXT) unit/test_blob.$(OBJEXT) \ - unit/test_cancel_query.$(OBJEXT) unit/test_column.$(OBJEXT) \ - unit/test_composite.$(OBJEXT) unit/test_connection.$(OBJEXT) \ - unit/test_cursor.$(OBJEXT) unit/test_encodings.$(OBJEXT) \ + unit/test_blob.$(OBJEXT) unit/test_cancel_query.$(OBJEXT) \ + unit/test_column.$(OBJEXT) unit/test_composite.$(OBJEXT) \ + unit/test_connection.$(OBJEXT) unit/test_cursor.$(OBJEXT) \ + unit/test_encodings.$(OBJEXT) \ unit/test_error_verbosity.$(OBJEXT) \ unit/test_errorhandler.$(OBJEXT) unit/test_escape.$(OBJEXT) \ unit/test_exceptions.$(OBJEXT) unit/test_field.$(OBJEXT) \ @@ -210,9 +210,7 @@ am__depfiles_remade = ./$(DEPDIR)/runner.Po ./$(DEPDIR)/test00.Po \ ./$(DEPDIR)/test82.Po ./$(DEPDIR)/test84.Po \ ./$(DEPDIR)/test87.Po ./$(DEPDIR)/test88.Po \ ./$(DEPDIR)/test89.Po ./$(DEPDIR)/test90.Po \ - unit/$(DEPDIR)/test_array.Po \ - unit/$(DEPDIR)/test_binarystring.Po \ - unit/$(DEPDIR)/test_blob.Po \ + unit/$(DEPDIR)/test_array.Po unit/$(DEPDIR)/test_blob.Po \ unit/$(DEPDIR)/test_cancel_query.Po \ unit/$(DEPDIR)/test_column.Po unit/$(DEPDIR)/test_composite.Po \ unit/$(DEPDIR)/test_connection.Po \ @@ -511,7 +509,6 @@ runner_SOURCES = \ test89.cxx \ test90.cxx \ unit/test_array.cxx \ - unit/test_binarystring.cxx \ unit/test_blob.cxx \ unit/test_cancel_query.cxx \ unit/test_column.cxx \ @@ -608,8 +605,6 @@ unit/$(DEPDIR)/$(am__dirstamp): @: > unit/$(DEPDIR)/$(am__dirstamp) unit/test_array.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) -unit/test_binarystring.$(OBJEXT): unit/$(am__dirstamp) \ - unit/$(DEPDIR)/$(am__dirstamp) unit/test_blob.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test_cancel_query.$(OBJEXT): unit/$(am__dirstamp) \ @@ -751,7 +746,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test89.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test90.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_array.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_binarystring.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_blob.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_cancel_query.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_column.Po@am__quote@ # am--include-marker @@ -1098,7 +1092,6 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/test89.Po -rm -f ./$(DEPDIR)/test90.Po -rm -f unit/$(DEPDIR)/test_array.Po - -rm -f unit/$(DEPDIR)/test_binarystring.Po -rm -f unit/$(DEPDIR)/test_blob.Po -rm -f unit/$(DEPDIR)/test_cancel_query.Po -rm -f unit/$(DEPDIR)/test_column.Po @@ -1230,7 +1223,6 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/test89.Po -rm -f ./$(DEPDIR)/test90.Po -rm -f unit/$(DEPDIR)/test_array.Po - -rm -f unit/$(DEPDIR)/test_binarystring.Po -rm -f unit/$(DEPDIR)/test_blob.Po -rm -f unit/$(DEPDIR)/test_cancel_query.Po -rm -f unit/$(DEPDIR)/test_column.Po diff --git a/test/unit/test_binarystring.cxx b/test/unit/test_binarystring.cxx deleted file mode 100644 index fb2449ad5..000000000 --- a/test/unit/test_binarystring.cxx +++ /dev/null @@ -1,216 +0,0 @@ -#include -#include -#include - -#include "../test_helpers.hxx" -#include "../test_types.hxx" - - -namespace -{ -pqxx::binarystring -make_binarystring(pqxx::transaction_base &T, std::string content) -{ -#include "pqxx/internal/ignore-deprecated-pre.hxx" - return pqxx::binarystring( - T.exec("SELECT " + T.quote_raw(content)).one_field()); -#include "pqxx/internal/ignore-deprecated-post.hxx" -} - - -void test_binarystring() -{ - pqxx::connection cx; - pqxx::work tx{cx}; - auto b{make_binarystring(tx, "")}; - PQXX_CHECK(std::empty(b), "Empty binarystring is not empty."); - PQXX_CHECK_EQUAL(b.str(), "", "Empty binarystring doesn't work."); - PQXX_CHECK_EQUAL(std::size(b), 0u, "Empty binarystring has nonzero size."); - PQXX_CHECK_EQUAL(b.length(), 0u, "Length/size mismatch."); - PQXX_CHECK(std::begin(b) == std::end(b), "Empty binarystring iterates."); - PQXX_CHECK( - std::cbegin(b) == std::begin(b), "Wrong cbegin for empty binarystring."); - PQXX_CHECK( - std::rbegin(b) == std::rend(b), "Empty binarystring reverse-iterates."); - PQXX_CHECK( - std::crbegin(b) == std::rbegin(b), - "Wrong crbegin for empty binarystring."); - PQXX_CHECK_THROWS( - b.at(0), std::out_of_range, "Empty binarystring accepts at()."); - - b = make_binarystring(tx, "z"); - PQXX_CHECK_EQUAL(b.str(), "z", "Basic nonempty binarystring is broken."); - PQXX_CHECK(not std::empty(b), "Nonempty binarystring is empty."); - PQXX_CHECK_EQUAL(std::size(b), 1u, "Bad binarystring size."); - PQXX_CHECK_EQUAL(b.length(), 1u, "Length/size mismatch."); - PQXX_CHECK( - std::begin(b) != std::end(b), "Nonempty binarystring does not iterate."); - PQXX_CHECK( - std::rbegin(b) != std::rend(b), - "Nonempty binarystring does not reverse-iterate."); - PQXX_CHECK(std::begin(b) + 1 == std::end(b), "Bad iteration."); - PQXX_CHECK(std::rbegin(b) + 1 == std::rend(b), "Bad reverse iteration."); - PQXX_CHECK(std::cbegin(b) == std::begin(b), "Wrong cbegin."); - PQXX_CHECK(std::cend(b) == std::end(b), "Wrong cend."); - PQXX_CHECK(std::crbegin(b) == std::rbegin(b), "Wrong crbegin."); - PQXX_CHECK(std::crend(b) == std::rend(b), "Wrong crend."); - PQXX_CHECK(b.front() == 'z', "Unexpected front()."); - PQXX_CHECK(b.back() == 'z', "Unexpected back()."); - PQXX_CHECK(b.at(0) == 'z', "Unexpected data at index 0."); - PQXX_CHECK_THROWS( - b.at(1), std::out_of_range, "Failed to catch range error."); - - std::string const simple{"ab"}; - b = make_binarystring(tx, simple); - PQXX_CHECK_EQUAL( - b.str(), simple, "Binary (un)escaping went wrong somewhere."); - PQXX_CHECK_EQUAL( - std::size(b), std::size(simple), "Escaping confuses length."); - - std::string const simple_escaped{tx.esc_raw(pqxx::bytes_view{ - reinterpret_cast(std::data(simple)), - std::size(simple)})}; - for (auto c : simple_escaped) - { - auto const uc{static_cast(c)}; - PQXX_CHECK(uc <= 127, "Non-ASCII byte in escaped string."); - } - -#include "pqxx/internal/ignore-deprecated-pre.hxx" - PQXX_CHECK_EQUAL( - tx.quote_raw( - reinterpret_cast(simple.c_str()), - std::size(simple)), - tx.quote(b), "quote_raw is broken"); - PQXX_CHECK_EQUAL( - tx.quote(b), tx.quote_raw(simple), "Binary quoting is broken."); - PQXX_CHECK_EQUAL( - pqxx::binarystring( - tx.query_value("SELECT $1", pqxx::params{b})) - .str(), - simple, "Binary string is not idempotent."); -#include "pqxx/internal/ignore-deprecated-post.hxx" - - std::string const bytes("\x01\x23\x23\xa1\x2b\x0c\xff"); - b = make_binarystring(tx, bytes); - PQXX_CHECK_EQUAL(b.str(), bytes, "Binary data breaks (un)escaping."); - - std::string const nully("a\0b", 3); - b = make_binarystring(tx, nully); - PQXX_CHECK_EQUAL(b.str(), nully, "Nul byte broke binary (un)escaping."); - PQXX_CHECK_EQUAL(std::size(b), 3u, "Nul byte broke binarystring size."); - - b = make_binarystring(tx, "foo"); - PQXX_CHECK_EQUAL(std::string(b.get(), 3), "foo", "get() appears broken."); - - auto b1{make_binarystring(tx, "1")}, b2{make_binarystring(tx, "2")}; - PQXX_CHECK_NOT_EQUAL(b1.get(), b2.get(), "Madness rules."); - PQXX_CHECK_NOT_EQUAL(b1.str(), b2.str(), "Logic has no more meaning."); - b1.swap(b2); - PQXX_CHECK_NOT_EQUAL(b1.str(), b2.str(), "swap() equalized binarystrings."); - PQXX_CHECK_NOT_EQUAL(b1.str(), "1", "swap() did not happen."); - PQXX_CHECK_EQUAL(b1.str(), "2", "swap() is broken."); - PQXX_CHECK_EQUAL(b2.str(), "1", "swap() went insane."); - - b = make_binarystring(tx, "bar"); - b.swap(b); - PQXX_CHECK_EQUAL(b.str(), "bar", "Self-swap confuses binarystring."); - - b = make_binarystring(tx, "\\x"); - PQXX_CHECK_EQUAL(b.str(), "\\x", "Hex-escape header confused (un)escaping."); -} - - -void test_binarystring_conversion() -{ - constexpr char bytes[]{"f\to\0o\n\0"}; - std::string_view const data{bytes, std::size(bytes) - 1}; -#include "pqxx/internal/ignore-deprecated-pre.hxx" - pqxx::binarystring bin{data}; -#include "pqxx/internal/ignore-deprecated-post.hxx" - auto const escaped{pqxx::to_string(bin)}; - PQXX_CHECK_EQUAL( - escaped, std::string_view{"\\x66096f006f0a00"}, "Unexpected hex escape."); - auto const restored{pqxx::from_string(escaped)}; - PQXX_CHECK_EQUAL( - std::size(restored), std::size(data), "Unescaping produced wrong length."); -} - - -void test_binarystring_stream() -{ - constexpr char bytes[]{"a\tb\0c"}; - std::string_view const data{bytes, std::size(bytes) - 1}; -#include "pqxx/internal/ignore-deprecated-pre.hxx" - pqxx::binarystring bin{data}; -#include "pqxx/internal/ignore-deprecated-post.hxx" - - pqxx::connection cx; - pqxx::transaction tx{cx}; - tx.exec("CREATE TEMP TABLE pqxxbinstream(id integer, bin bytea)").no_rows(); - - auto to{pqxx::stream_to::table(tx, {"pqxxbinstream"})}; - to.write_values(0, bin); - to.complete(); - - auto ptr{reinterpret_cast(std::data(data))}; - auto expect{tx.quote(pqxx::bytes_view{ptr, std::size(data)})}; - PQXX_CHECK( - tx.query_value("SELECT bin = " + expect + " FROM pqxxbinstream"), - "binarystring did not stream_to properly."); - PQXX_CHECK_EQUAL( - tx.query_value("SELECT octet_length(bin) FROM pqxxbinstream"), - std::size(data), "Did the terminating zero break the bytea?"); -} - - -void test_binarystring_array_stream() -{ - // This test won't compile on clang in maintainer mode. For some reason, - // clang seems to ignore the ignore-deprecated headers in just this one - // function, where we create the vector of binarystring. -#if !defined(__clang__) - pqxx::connection cx; - pqxx::transaction tx{cx}; - tx.exec("CREATE TEMP TABLE pqxxbinstream(id integer, vec bytea[])") - .no_rows(); - - constexpr char bytes1[]{"a\tb\0c"}, bytes2[]{"1\0.2"}; - std::string_view const data1{bytes1}, data2{bytes2}; -# include "pqxx/internal/ignore-deprecated-pre.hxx" - pqxx::binarystring bin1{data1}, bin2{data2}; - std::vector const vec{bin1, bin2}; -# include "pqxx/internal/ignore-deprecated-post.hxx" - - auto to{pqxx::stream_to::table(tx, {"pqxxbinstream"})}; - to.write_values(0, vec); - to.complete(); - - PQXX_CHECK_EQUAL( - tx.query_value( - "SELECT array_length(vec, 1) FROM pqxxbinstream"), - std::size(vec), "Array came out with wrong length."); - - auto ptr1{reinterpret_cast(std::data(data1))}, - ptr2{reinterpret_cast(std::data(data2))}; - auto expect1{tx.quote(pqxx::bytes_view{ptr1, std::size(data1)})}, - expect2{tx.quote(pqxx::bytes_view{ptr2, std::size(data2)})}; - PQXX_CHECK( - tx.query_value("SELECT vec[1] = " + expect1 + " FROM pqxxbinstream"), - "Bytea in array came out wrong."); - PQXX_CHECK( - tx.query_value("SELECT vec[2] = " + expect2 + " FROM pqxxbinstream"), - "First bytea in array worked, but second did not."); - PQXX_CHECK_EQUAL( - tx.query_value( - "SELECT octet_length(vec[1]) FROM pqxxbinstream"), - std::size(data1), "Bytea length broke inside array."); -#endif // __clang__ -} - - -PQXX_REGISTER_TEST(test_binarystring); -PQXX_REGISTER_TEST(test_binarystring_conversion); -PQXX_REGISTER_TEST(test_binarystring_stream); -PQXX_REGISTER_TEST(test_binarystring_array_stream); -} // namespace diff --git a/test/unit/test_prepared_statement.cxx b/test/unit/test_prepared_statement.cxx index 5a307b974..9d435e4ea 100644 --- a/test/unit/test_prepared_statement.cxx +++ b/test/unit/test_prepared_statement.cxx @@ -177,16 +177,6 @@ void test_binary() constexpr char raw_bytes[]{"Binary\0bytes'\"with\tweird\xff bytes"}; std::string const input{raw_bytes, std::size(raw_bytes)}; -#include "pqxx/internal/ignore-deprecated-pre.hxx" - { - pqxx::binarystring const bin{input}; - auto rw{tx.exec(pqxx::prepped{"EchoBin"}, pqxx::params{bin}).one_row()}; - PQXX_CHECK_EQUAL( - pqxx::binarystring(rw[0]).str(), input, - "Binary string came out damaged."); - } -#include "pqxx/internal/ignore-deprecated-post.hxx" - { pqxx::bytes bytes{ reinterpret_cast(raw_bytes), std::size(raw_bytes)}; From beae65001f026b6250e410ba75ab016e84e3ea58 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 23 Dec 2024 15:50:47 +0100 Subject: [PATCH 20/57] Retire result row slicing. This was a long-forgotten feature that somebody thought might be a good idea, around 20 years ago. It's only been deprecated for a short time, but it's so obscure that I stronly doubt that anyone has ever used it at all. Meanwhile it was weighing down a pretty basic piece of code that comes up in lots of loops, so... less is more. --- NEWS | 1 + include/pqxx/row.hxx | 23 +---- src/row.cxx | 54 ++-------- test/Makefile.am | 1 - test/Makefile.in | 10 +- test/unit/test_result_slicing.cxx | 157 ------------------------------ 6 files changed, 11 insertions(+), 235 deletions(-) delete mode 100644 test/unit/test_result_slicing.cxx diff --git a/NEWS b/NEWS index 3588f21c1..abf953e96 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ - Assume compiler supports integral conversions in `charconv`. - Assume compiler supports spans, ranges, and `cmp_less` etc. - Retired `binarystring` and its headers. + - Retired result row slicing. 7.10.1 - Fix string conversion buffer budget for arrays containing nulls. (#921) - Remove `-fanalyzer` option again; gcc is still broken. diff --git a/include/pqxx/row.hxx b/include/pqxx/row.hxx index 704b8bc2e..ab445d666 100644 --- a/include/pqxx/row.hxx +++ b/include/pqxx/row.hxx @@ -105,7 +105,7 @@ public: [[nodiscard]] constexpr size_type size() const noexcept { - return m_end - m_begin; + return m_end; } /// Row number, assuming this is a real row and not end()/rend(). @@ -194,24 +194,6 @@ public: [[deprecated("Swap iterators, not rows.")]] void swap(row &) noexcept; - /** Produce a slice of this row, containing the given range of columns. - * - * @deprecated I haven't heard of anyone caring about row slicing at all in - * at least the last 15 years. Yet it adds complexity, so unless anyone - * files a bug explaining why they really need this feature, I'm going to - * remove it. Even if they do, the feature may need an update. - * - * The slice runs from the range's starting column to the range's end - * column, exclusive. It looks just like a normal result row, except - * slices can be empty. - */ - [[deprecated("Row slicing is going away. File a bug if you need it.")]] row - slice(size_type sbegin, size_type send) const; - - /// Is this a row without fields? Can only happen to a slice. - [[nodiscard, deprecated("Row slicing is going away.")]] PQXX_PURE bool - empty() const noexcept; - protected: friend class const_row_iterator; friend class result; @@ -255,9 +237,6 @@ protected: */ result::size_type m_index = 0; - // TODO: Remove m_begin and (if possible) m_end when we remove slice(). - /// First column in slice. This row ignores lower-numbered columns. - size_type m_begin = 0; /// End column in slice. This row only sees lower-numbered columns. size_type m_end = 0; diff --git a/src/row.cxx b/src/row.cxx index 4e51efa88..63972314c 100644 --- a/src/row.cxx +++ b/src/row.cxx @@ -34,7 +34,7 @@ pqxx::row::row(result r, result::size_type index, size_type cols) noexcept : pqxx::row::const_iterator pqxx::row::begin() const noexcept { - return {*this, m_begin}; + return {*this, 0}; } @@ -58,7 +58,7 @@ pqxx::row::const_iterator pqxx::row::cend() const noexcept pqxx::row::reference pqxx::row::front() const noexcept { - return field{m_result, m_index, m_begin}; + return field{m_result, m_index, 0}; } @@ -108,7 +108,7 @@ bool pqxx::row::operator==(row const &rhs) const noexcept pqxx::row::reference pqxx::row::operator[](size_type i) const noexcept { - return field{m_result, m_index, m_begin + i}; + return field{m_result, m_index, i}; } @@ -121,21 +121,18 @@ pqxx::row::reference pqxx::row::operator[](zview col_name) const void pqxx::row::swap(row &rhs) noexcept { auto const i{m_index}; - auto const b{m_begin}; auto const e{m_end}; m_result.swap(rhs.m_result); m_index = rhs.m_index; - m_begin = rhs.m_begin; m_end = rhs.m_end; rhs.m_index = i; - rhs.m_begin = b; rhs.m_end = e; } pqxx::field pqxx::row::at(zview col_name) const { - return {m_result, m_index, m_begin + column_number(col_name)}; + return {m_result, m_index, column_number(col_name)}; } @@ -150,60 +147,25 @@ pqxx::field pqxx::row::at(pqxx::row::size_type i) const pqxx::oid pqxx::row::column_type(size_type col_num) const { - return m_result.column_type(m_begin + col_num); + return m_result.column_type(col_num); } pqxx::oid pqxx::row::column_table(size_type col_num) const { - return m_result.column_table(m_begin + col_num); + return m_result.column_table(col_num); } pqxx::row::size_type pqxx::row::table_column(size_type col_num) const { - return m_result.table_column(m_begin + col_num); + return m_result.table_column(col_num); } pqxx::row::size_type pqxx::row::column_number(zview col_name) const { - auto const n{m_result.column_number(col_name)}; - if (n >= m_end) - throw argument_error{ - "Column '" + std::string{col_name} + "' falls outside slice."}; - if (n >= m_begin) - return n - m_begin; - - // This deals with a really nasty possibility: that the column name occurs - // twice - once before the beginning of the slice, and once inside the slice. - char const *const adapted_name{m_result.column_name(n)}; - for (auto i{m_begin}; i < m_end; ++i) - if (strcmp(adapted_name, m_result.column_name(i)) == 0) - return i - m_begin; - - // Didn't find any? Recurse just to produce the same error message. - return result{}.column_number(col_name); -} - - -pqxx::row PQXX_COLD pqxx::row::slice(size_type sbegin, size_type send) const -{ - if (sbegin > send or send > size()) - throw range_error{"Invalid field range."}; - -#include "pqxx/internal/ignore-deprecated-pre.hxx" - row res{*this}; -#include "pqxx/internal/ignore-deprecated-post.hxx" - res.m_begin = m_begin + sbegin; - res.m_end = m_begin + send; - return res; -} - - -bool PQXX_COLD pqxx::row::empty() const noexcept -{ - return m_begin == m_end; + return m_result.column_number(col_name); } diff --git a/test/Makefile.am b/test/Makefile.am index b00413d6e..30d8fed12 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -101,7 +101,6 @@ runner_SOURCES = \ unit/test_range.cxx \ unit/test_read_transaction.cxx \ unit/test_result_iteration.cxx \ - unit/test_result_slicing.cxx \ unit/test_row.cxx \ unit/test_separated_list.cxx \ unit/test_simultaneous_transactions.cxx \ diff --git a/test/Makefile.in b/test/Makefile.in index 2f8d43afd..fd670227e 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -152,8 +152,7 @@ am_runner_OBJECTS = test00.$(OBJEXT) test01.$(OBJEXT) test02.$(OBJEXT) \ unit/test_notification.$(OBJEXT) unit/test_pipeline.$(OBJEXT) \ unit/test_prepared_statement.$(OBJEXT) \ unit/test_range.$(OBJEXT) unit/test_read_transaction.$(OBJEXT) \ - unit/test_result_iteration.$(OBJEXT) \ - unit/test_result_slicing.$(OBJEXT) unit/test_row.$(OBJEXT) \ + unit/test_result_iteration.$(OBJEXT) unit/test_row.$(OBJEXT) \ unit/test_separated_list.$(OBJEXT) \ unit/test_simultaneous_transactions.$(OBJEXT) \ unit/test_sql_cursor.$(OBJEXT) \ @@ -229,7 +228,6 @@ am__depfiles_remade = ./$(DEPDIR)/runner.Po ./$(DEPDIR)/test00.Po \ unit/$(DEPDIR)/test_range.Po \ unit/$(DEPDIR)/test_read_transaction.Po \ unit/$(DEPDIR)/test_result_iteration.Po \ - unit/$(DEPDIR)/test_result_slicing.Po \ unit/$(DEPDIR)/test_row.Po \ unit/$(DEPDIR)/test_separated_list.Po \ unit/$(DEPDIR)/test_simultaneous_transactions.Po \ @@ -531,7 +529,6 @@ runner_SOURCES = \ unit/test_range.cxx \ unit/test_read_transaction.cxx \ unit/test_result_iteration.cxx \ - unit/test_result_slicing.cxx \ unit/test_row.cxx \ unit/test_separated_list.cxx \ unit/test_simultaneous_transactions.cxx \ @@ -649,8 +646,6 @@ unit/test_read_transaction.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test_result_iteration.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) -unit/test_result_slicing.$(OBJEXT): unit/$(am__dirstamp) \ - unit/$(DEPDIR)/$(am__dirstamp) unit/test_row.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test_separated_list.$(OBJEXT): unit/$(am__dirstamp) \ @@ -768,7 +763,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_range.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_read_transaction.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_result_iteration.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_result_slicing.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_row.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_separated_list.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test_simultaneous_transactions.Po@am__quote@ # am--include-marker @@ -1114,7 +1108,6 @@ distclean: distclean-am -rm -f unit/$(DEPDIR)/test_range.Po -rm -f unit/$(DEPDIR)/test_read_transaction.Po -rm -f unit/$(DEPDIR)/test_result_iteration.Po - -rm -f unit/$(DEPDIR)/test_result_slicing.Po -rm -f unit/$(DEPDIR)/test_row.Po -rm -f unit/$(DEPDIR)/test_separated_list.Po -rm -f unit/$(DEPDIR)/test_simultaneous_transactions.Po @@ -1245,7 +1238,6 @@ maintainer-clean: maintainer-clean-am -rm -f unit/$(DEPDIR)/test_range.Po -rm -f unit/$(DEPDIR)/test_read_transaction.Po -rm -f unit/$(DEPDIR)/test_result_iteration.Po - -rm -f unit/$(DEPDIR)/test_result_slicing.Po -rm -f unit/$(DEPDIR)/test_row.Po -rm -f unit/$(DEPDIR)/test_separated_list.Po -rm -f unit/$(DEPDIR)/test_simultaneous_transactions.Po diff --git a/test/unit/test_result_slicing.cxx b/test/unit/test_result_slicing.cxx deleted file mode 100644 index c7fb92f40..000000000 --- a/test/unit/test_result_slicing.cxx +++ /dev/null @@ -1,157 +0,0 @@ -#include - -#include "../test_helpers.hxx" - -#include "pqxx/internal/ignore-deprecated-pre.hxx" - -namespace pqxx -{ -template<> struct nullness : no_null -{}; - -template<> -struct nullness - : no_null -{}; - - -template<> struct string_traits -{ - static constexpr zview text{"[row::const_iterator]"}; - static zview to_buf(char *, char *, row::const_iterator const &) - { - return text; - } - static char *into_buf(char *begin, char *end, row::const_iterator const &) - { - if ((end - begin) <= 30) - throw conversion_overrun{"Not enough buffer for const row iterator."}; - std::memcpy(begin, text.c_str(), std::size(text) + 1); - return begin + std::size(text); - } - static constexpr std::size_t - size_buffer(row::const_iterator const &) noexcept - { - return std::size(text) + 1; - } -}; - - -template<> struct string_traits -{ - static constexpr zview text{"[row::const_reverse_iterator]"}; - static pqxx::zview - to_buf(char *, char *, row::const_reverse_iterator const &) - { - return text; - } - static char * - into_buf(char *begin, char *end, row::const_reverse_iterator const &) - { - if ((end - begin) <= 30) - throw conversion_overrun{"Not enough buffer for const row iterator."}; - std::memcpy(begin, text.c_str(), std::size(text) + 1); - return begin + std::size(text); - } - static constexpr std::size_t - size_buffer(row::const_reverse_iterator const &) noexcept - { - return 100; - } -}; -} // namespace pqxx - -namespace -{ -void test_result_slicing() -{ - pqxx::connection cx; - pqxx::work tx{cx}; - auto r{tx.exec("SELECT 1")}; - - PQXX_CHECK(not std::empty(r[0]), "A plain row shows up as empty."); - - // Empty slice at beginning of row. - pqxx::row s{r[0].slice(0, 0)}; - PQXX_CHECK(std::empty(s), "Empty slice does not show up as empty."); - PQXX_CHECK_EQUAL(std::size(s), 0, "Slicing produces wrong row size."); - PQXX_CHECK_EQUAL( - std::begin(s), std::end(s), "Slice begin()/end() are broken."); - PQXX_CHECK_EQUAL( - std::rbegin(s), std::rend(s), "Slice rbegin()/rend() are broken."); - - PQXX_CHECK_THROWS(s.at(0), pqxx::range_error, "at() does not throw."); - pqxx::row slice; - PQXX_CHECK_THROWS( - slice = r[0].slice(0, 2), pqxx::range_error, "No range check."); - pqxx::ignore_unused(slice); - PQXX_CHECK_THROWS( - slice = r[0].slice(1, 0), pqxx::range_error, "Can reverse-slice."); - pqxx::ignore_unused(slice); - - // Empty slice at end of row. - s = r[0].slice(1, 1); - PQXX_CHECK(std::empty(s), "empty() is broken."); - PQXX_CHECK_EQUAL(std::size(s), 0, "size() is broken."); - PQXX_CHECK_EQUAL(std::begin(s), std::end(s), "begin()/end() are broken."); - PQXX_CHECK_EQUAL( - std::rbegin(s), std::rend(s), "rbegin()/rend() are broken."); - - PQXX_CHECK_THROWS(s.at(0), pqxx::range_error, "at() is inconsistent."); - - // Slice that matches the entire row. - s = r[0].slice(0, 1); - PQXX_CHECK(not std::empty(s), "Nonempty slice shows up as empty."); - PQXX_CHECK_EQUAL(std::size(s), 1, "size() breaks for non-empty slice."); - PQXX_CHECK_EQUAL(std::begin(s) + 1, std::end(s), "Iteration is broken."); - PQXX_CHECK_EQUAL( - std::rbegin(s) + 1, std::rend(s), "Reverse iteration is broken."); - PQXX_CHECK_EQUAL(s.at(0).as(), 1, "Accessing a slice is broken."); - PQXX_CHECK_EQUAL(s[0].as(), 1, "operator[] is broken."); - PQXX_CHECK_THROWS(s.at(1).as(), pqxx::range_error, "at() is off."); - - // Meaningful slice at beginning of row. - r = tx.exec("SELECT 1, 2, 3"); - s = r[0].slice(0, 1); - PQXX_CHECK(not std::empty(s), "Slicing confuses empty()."); - PQXX_CHECK_THROWS( - s.at(1).as(), pqxx::range_error, "at() does not enforce slice."); - - // Meaningful slice that skips an initial column. - s = r[0].slice(1, 2); - PQXX_CHECK( - not std::empty(s), "Slicing away leading columns confuses empty()."); - PQXX_CHECK_EQUAL(s[0].as(), 2, "Slicing offset is broken."); - PQXX_CHECK_EQUAL( - std::begin(s)->as(), 2, "Iteration uses wrong offset."); - PQXX_CHECK_EQUAL( - std::begin(s) + 1, std::end(s), "Iteration has wrong range."); - PQXX_CHECK_EQUAL( - std::rbegin(s) + 1, std::rend(s), "Reverse iteration has wrong range."); - PQXX_CHECK_THROWS( - s.at(1).as(), pqxx::range_error, "Offset slicing is broken."); - - // Column names in a slice. - r = tx.exec("SELECT 1 AS one, 2 AS two, 3 AS three"); - s = r[0].slice(1, 2); - PQXX_CHECK_EQUAL(s["two"].as(), 2, "Column addressing breaks."); - PQXX_CHECK_THROWS( - pqxx::ignore_unused(s.column_number("one")), pqxx::argument_error, - "Can access column name before slice."); - PQXX_CHECK_THROWS( - pqxx::ignore_unused(s.column_number("three")), pqxx::argument_error, - "Can access column name after slice."); - PQXX_CHECK_EQUAL( - s.column_number("Two"), 0, "Column name is case sensitive."); - - // Identical column names. - r = tx.exec("SELECT 1 AS x, 2 AS x"); - s = r[0].slice(1, 2); - PQXX_CHECK_EQUAL(s["x"].as(), 2, "Identical column names break slice."); -} - - -PQXX_REGISTER_TEST(test_result_slicing); -} // namespace - -#include "pqxx/internal/ignore-deprecated-post.hxx" From 71bf33dd7bc53292cb7ed7b2c245cdcb26fafb94 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Tue, 24 Dec 2024 11:39:30 +0100 Subject: [PATCH 21/57] Docstring. --- include/pqxx/row.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pqxx/row.hxx b/include/pqxx/row.hxx index ab445d666..8467beb36 100644 --- a/include/pqxx/row.hxx +++ b/include/pqxx/row.hxx @@ -237,7 +237,7 @@ protected: */ result::size_type m_index = 0; - /// End column in slice. This row only sees lower-numbered columns. + /// Number of columns in the row. size_type m_end = 0; private: From 55ae6dd9e9d3ace7221a7d9f85984f2701a68f81 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Tue, 24 Dec 2024 15:30:57 +0100 Subject: [PATCH 22/57] Does tweaking configure cmd line work? --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 3d66102d9..758d991d6 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -12,7 +12,7 @@ build: - "graphviz" jobs: pre_build: - - "./configure --enable-documentation CXXFLAGS='-O0 -std=c++23'" + - "./configure CXXFLAGS='-O0 -std=c++23' --enable-documentation" - "mkdir -p doc/doxygen-html" - "cd doc && doxygen" - "mkdir -p -- $READTHEDOCS_OUTPUT/html" From 52a5b5d7cadd321b5aeed85e581c2442b3b5a13b Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 26 Dec 2024 23:28:00 +0100 Subject: [PATCH 23/57] Log versions. --- .readthedocs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 758d991d6..58542eba0 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -12,6 +12,7 @@ build: - "graphviz" jobs: pre_build: + - "lsb_release -a && gcc --version" - "./configure CXXFLAGS='-O0 -std=c++23' --enable-documentation" - "mkdir -p doc/doxygen-html" - "cd doc && doxygen" From 131f39ed8992acc8a821d7c5e99775f07c90c096 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 26 Dec 2024 23:38:46 +0100 Subject: [PATCH 24/57] Try clang. --- .readthedocs.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 58542eba0..6f498f0f5 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -12,8 +12,8 @@ build: - "graphviz" jobs: pre_build: - - "lsb_release -a && gcc --version" - - "./configure CXXFLAGS='-O0 -std=c++23' --enable-documentation" + - "lsb_release -a && g++ --version && clang++ --version" + - "./configure CXX=clang++ CXXFLAGS='-O0 -std=c++23' --enable-documentation" - "mkdir -p doc/doxygen-html" - "cd doc && doxygen" - "mkdir -p -- $READTHEDOCS_OUTPUT/html" From 272d8980e863c63b822ae07f736bc22b8ad2c32b Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 26 Dec 2024 23:40:22 +0100 Subject: [PATCH 25/57] Install clang & gcc. --- .readthedocs.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 6f498f0f5..80b449c58 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,6 +9,8 @@ build: tools: python: "3.12" apt_packages: + - "clang" + - "gcc" - "graphviz" jobs: pre_build: From 45f3c61f88a94424721ae026381c1b1a463525b9 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 26 Dec 2024 23:50:13 +0100 Subject: [PATCH 26/57] Install libpq-dev in doc build. --- .readthedocs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 80b449c58..a85e5f7c5 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -12,6 +12,7 @@ build: - "clang" - "gcc" - "graphviz" + - "libpq-dev" jobs: pre_build: - "lsb_release -a && g++ --version && clang++ --version" From 479b504be086a38eb74a23d1e73789178b5fdaf5 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 26 Dec 2024 23:54:32 +0100 Subject: [PATCH 27/57] Install build-essential; back to gcc. --- .readthedocs.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index a85e5f7c5..1bf60d8d4 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,14 +9,13 @@ build: tools: python: "3.12" apt_packages: - - "clang" - - "gcc" + - "build-essential" - "graphviz" - "libpq-dev" jobs: pre_build: - "lsb_release -a && g++ --version && clang++ --version" - - "./configure CXX=clang++ CXXFLAGS='-O0 -std=c++23' --enable-documentation" + - "./configure CXXFLAGS='-O0 -std=c++23' --enable-documentation" - "mkdir -p doc/doxygen-html" - "cd doc && doxygen" - "mkdir -p -- $READTHEDOCS_OUTPUT/html" From a9516474618d3a3ce22c5f98b2735b976d47b908 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 26 Dec 2024 23:55:52 +0100 Subject: [PATCH 28/57] Not installing clang, so don't run it. --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 1bf60d8d4..b6aeb2b1d 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -14,7 +14,7 @@ build: - "libpq-dev" jobs: pre_build: - - "lsb_release -a && g++ --version && clang++ --version" + - "lsb_release -a && g++ --version" - "./configure CXXFLAGS='-O0 -std=c++23' --enable-documentation" - "mkdir -p doc/doxygen-html" - "cd doc && doxygen" From 6109ef46fe79565ea43452afc3e96024eff62573 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Sun, 29 Dec 2024 03:52:18 +0100 Subject: [PATCH 29/57] Try installing more on readthedocs. --- .readthedocs.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index b6aeb2b1d..139e42e35 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,9 +9,12 @@ build: tools: python: "3.12" apt_packages: + - "autoconf" + - "automake" - "build-essential" - "graphviz" - "libpq-dev" + - "make" jobs: pre_build: - "lsb_release -a && g++ --version" From d92db7ee199237a38ad8cc1d03f9aa01acd4dc7c Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 30 Dec 2024 00:34:43 +0100 Subject: [PATCH 30/57] Just one option in CXXFLAGS. --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 139e42e35..3b8b56afd 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -18,7 +18,7 @@ build: jobs: pre_build: - "lsb_release -a && g++ --version" - - "./configure CXXFLAGS='-O0 -std=c++23' --enable-documentation" + - "./configure CXXFLAGS=-std=c++23 --enable-documentation" - "mkdir -p doc/doxygen-html" - "cd doc && doxygen" - "mkdir -p -- $READTHEDOCS_OUTPUT/html" From 9e0cbbdd3bc1547db249061ac0756b7953f266e2 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 30 Dec 2024 00:37:16 +0100 Subject: [PATCH 31/57] Try double quotes. --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 3b8b56afd..86940d634 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -18,7 +18,7 @@ build: jobs: pre_build: - "lsb_release -a && g++ --version" - - "./configure CXXFLAGS=-std=c++23 --enable-documentation" + - "./configure CXXFLAGS=\"-O0 -std=c++23\" --enable-documentation" - "mkdir -p doc/doxygen-html" - "cd doc && doxygen" - "mkdir -p -- $READTHEDOCS_OUTPUT/html" From e34e2eb781aacc9eebc40c5b4c75abbf95711705 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 30 Dec 2024 00:51:18 +0100 Subject: [PATCH 32/57] Wait for notfication faster. --- config/Makefile.in | 2 +- test/unit/test_notification.cxx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/Makefile.in b/config/Makefile.in index 51586bda2..9b1454986 100644 --- a/config/Makefile.in +++ b/config/Makefile.in @@ -124,7 +124,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/test/unit/test_notification.cxx b/test/unit/test_notification.cxx index c98a85c9b..2c38fbb88 100644 --- a/test/unit/test_notification.cxx +++ b/test/unit/test_notification.cxx @@ -57,8 +57,8 @@ void test_receive_classic( tx.commit(); int notifs{0}; - for (int i{0}; (i < 10) and (notifs == 0); - ++i, pqxx::internal::wait_for(1000u)) + for (int i{0}; (i < 100) and (notifs == 0); + ++i, pqxx::internal::wait_for(100u)) notifs = cx.get_notifs(); PQXX_CHECK_EQUAL(notifs, 1, "Got wrong number of notifications."); From e7501e05a96c12aa610c9c77bcb7ac6d1e80a0ed Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 30 Dec 2024 00:58:01 +0100 Subject: [PATCH 33/57] Drop now-redundant `test04.cxx`. --- test/Makefile.am | 1 - test/Makefile.in | 70 +++++++++++++++++++++++------------------------- test/test04.cxx | 55 ------------------------------------- 3 files changed, 33 insertions(+), 93 deletions(-) delete mode 100644 test/test04.cxx diff --git a/test/Makefile.am b/test/Makefile.am index 30d8fed12..aee6936b2 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -40,7 +40,6 @@ runner_SOURCES = \ test00.cxx \ test01.cxx \ test02.cxx \ - test04.cxx \ test07.cxx \ test10.cxx \ test11.cxx \ diff --git a/test/Makefile.in b/test/Makefile.in index fd670227e..678b6a532 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -126,19 +126,19 @@ CONFIG_CLEAN_VPATH_FILES = am__EXEEXT_1 = runner$(EXEEXT) am__dirstamp = $(am__leading_dot)dirstamp am_runner_OBJECTS = test00.$(OBJEXT) test01.$(OBJEXT) test02.$(OBJEXT) \ - test04.$(OBJEXT) test07.$(OBJEXT) test10.$(OBJEXT) \ - test11.$(OBJEXT) test13.$(OBJEXT) test14.$(OBJEXT) \ - test16.$(OBJEXT) test17.$(OBJEXT) test18.$(OBJEXT) \ - test20.$(OBJEXT) test21.$(OBJEXT) test26.$(OBJEXT) \ - test29.$(OBJEXT) test30.$(OBJEXT) test32.$(OBJEXT) \ - test37.$(OBJEXT) test39.$(OBJEXT) test46.$(OBJEXT) \ - test56.$(OBJEXT) test60.$(OBJEXT) test61.$(OBJEXT) \ - test62.$(OBJEXT) test69.$(OBJEXT) test70.$(OBJEXT) \ - test71.$(OBJEXT) test72.$(OBJEXT) test74.$(OBJEXT) \ - test75.$(OBJEXT) test76.$(OBJEXT) test77.$(OBJEXT) \ - test78.$(OBJEXT) test79.$(OBJEXT) test82.$(OBJEXT) \ - test84.$(OBJEXT) test87.$(OBJEXT) test88.$(OBJEXT) \ - test89.$(OBJEXT) test90.$(OBJEXT) unit/test_array.$(OBJEXT) \ + test07.$(OBJEXT) test10.$(OBJEXT) test11.$(OBJEXT) \ + test13.$(OBJEXT) test14.$(OBJEXT) test16.$(OBJEXT) \ + test17.$(OBJEXT) test18.$(OBJEXT) test20.$(OBJEXT) \ + test21.$(OBJEXT) test26.$(OBJEXT) test29.$(OBJEXT) \ + test30.$(OBJEXT) test32.$(OBJEXT) test37.$(OBJEXT) \ + test39.$(OBJEXT) test46.$(OBJEXT) test56.$(OBJEXT) \ + test60.$(OBJEXT) test61.$(OBJEXT) test62.$(OBJEXT) \ + test69.$(OBJEXT) test70.$(OBJEXT) test71.$(OBJEXT) \ + test72.$(OBJEXT) test74.$(OBJEXT) test75.$(OBJEXT) \ + test76.$(OBJEXT) test77.$(OBJEXT) test78.$(OBJEXT) \ + test79.$(OBJEXT) test82.$(OBJEXT) test84.$(OBJEXT) \ + test87.$(OBJEXT) test88.$(OBJEXT) test89.$(OBJEXT) \ + test90.$(OBJEXT) unit/test_array.$(OBJEXT) \ unit/test_blob.$(OBJEXT) unit/test_cancel_query.$(OBJEXT) \ unit/test_column.$(OBJEXT) unit/test_composite.$(OBJEXT) \ unit/test_connection.$(OBJEXT) unit/test_cursor.$(OBJEXT) \ @@ -190,26 +190,26 @@ depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/runner.Po ./$(DEPDIR)/test00.Po \ ./$(DEPDIR)/test01.Po ./$(DEPDIR)/test02.Po \ - ./$(DEPDIR)/test04.Po ./$(DEPDIR)/test07.Po \ - ./$(DEPDIR)/test10.Po ./$(DEPDIR)/test11.Po \ - ./$(DEPDIR)/test13.Po ./$(DEPDIR)/test14.Po \ - ./$(DEPDIR)/test16.Po ./$(DEPDIR)/test17.Po \ - ./$(DEPDIR)/test18.Po ./$(DEPDIR)/test20.Po \ - ./$(DEPDIR)/test21.Po ./$(DEPDIR)/test26.Po \ - ./$(DEPDIR)/test29.Po ./$(DEPDIR)/test30.Po \ - ./$(DEPDIR)/test32.Po ./$(DEPDIR)/test37.Po \ - ./$(DEPDIR)/test39.Po ./$(DEPDIR)/test46.Po \ - ./$(DEPDIR)/test56.Po ./$(DEPDIR)/test60.Po \ - ./$(DEPDIR)/test61.Po ./$(DEPDIR)/test62.Po \ - ./$(DEPDIR)/test69.Po ./$(DEPDIR)/test70.Po \ - ./$(DEPDIR)/test71.Po ./$(DEPDIR)/test72.Po \ - ./$(DEPDIR)/test74.Po ./$(DEPDIR)/test75.Po \ - ./$(DEPDIR)/test76.Po ./$(DEPDIR)/test77.Po \ - ./$(DEPDIR)/test78.Po ./$(DEPDIR)/test79.Po \ - ./$(DEPDIR)/test82.Po ./$(DEPDIR)/test84.Po \ - ./$(DEPDIR)/test87.Po ./$(DEPDIR)/test88.Po \ - ./$(DEPDIR)/test89.Po ./$(DEPDIR)/test90.Po \ - unit/$(DEPDIR)/test_array.Po unit/$(DEPDIR)/test_blob.Po \ + ./$(DEPDIR)/test07.Po ./$(DEPDIR)/test10.Po \ + ./$(DEPDIR)/test11.Po ./$(DEPDIR)/test13.Po \ + ./$(DEPDIR)/test14.Po ./$(DEPDIR)/test16.Po \ + ./$(DEPDIR)/test17.Po ./$(DEPDIR)/test18.Po \ + ./$(DEPDIR)/test20.Po ./$(DEPDIR)/test21.Po \ + ./$(DEPDIR)/test26.Po ./$(DEPDIR)/test29.Po \ + ./$(DEPDIR)/test30.Po ./$(DEPDIR)/test32.Po \ + ./$(DEPDIR)/test37.Po ./$(DEPDIR)/test39.Po \ + ./$(DEPDIR)/test46.Po ./$(DEPDIR)/test56.Po \ + ./$(DEPDIR)/test60.Po ./$(DEPDIR)/test61.Po \ + ./$(DEPDIR)/test62.Po ./$(DEPDIR)/test69.Po \ + ./$(DEPDIR)/test70.Po ./$(DEPDIR)/test71.Po \ + ./$(DEPDIR)/test72.Po ./$(DEPDIR)/test74.Po \ + ./$(DEPDIR)/test75.Po ./$(DEPDIR)/test76.Po \ + ./$(DEPDIR)/test77.Po ./$(DEPDIR)/test78.Po \ + ./$(DEPDIR)/test79.Po ./$(DEPDIR)/test82.Po \ + ./$(DEPDIR)/test84.Po ./$(DEPDIR)/test87.Po \ + ./$(DEPDIR)/test88.Po ./$(DEPDIR)/test89.Po \ + ./$(DEPDIR)/test90.Po unit/$(DEPDIR)/test_array.Po \ + unit/$(DEPDIR)/test_blob.Po \ unit/$(DEPDIR)/test_cancel_query.Po \ unit/$(DEPDIR)/test_column.Po unit/$(DEPDIR)/test_composite.Po \ unit/$(DEPDIR)/test_connection.Po \ @@ -468,7 +468,6 @@ runner_SOURCES = \ test00.cxx \ test01.cxx \ test02.cxx \ - test04.cxx \ test07.cxx \ test10.cxx \ test11.cxx \ @@ -702,7 +701,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test00.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test01.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test02.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test04.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test07.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test10.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test11.Po@am__quote@ # am--include-marker @@ -1047,7 +1045,6 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/test00.Po -rm -f ./$(DEPDIR)/test01.Po -rm -f ./$(DEPDIR)/test02.Po - -rm -f ./$(DEPDIR)/test04.Po -rm -f ./$(DEPDIR)/test07.Po -rm -f ./$(DEPDIR)/test10.Po -rm -f ./$(DEPDIR)/test11.Po @@ -1177,7 +1174,6 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/test00.Po -rm -f ./$(DEPDIR)/test01.Po -rm -f ./$(DEPDIR)/test02.Po - -rm -f ./$(DEPDIR)/test04.Po -rm -f ./$(DEPDIR)/test07.Po -rm -f ./$(DEPDIR)/test10.Po -rm -f ./$(DEPDIR)/test11.Po diff --git a/test/test04.cxx b/test/test04.cxx deleted file mode 100644 index b59655a98..000000000 --- a/test/test04.cxx +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include -#include -#include - -#include - -#include - -#include - -#include -#include - -#include "test_helpers.hxx" - -// Example program for libpqxx. Send notification to self. - -namespace -{ -void test_004() -{ - auto const channel{"pqxx_test_notif"}; - pqxx::connection cx; - int backend_pid{0}; - cx.listen(channel, [&backend_pid](pqxx::notification n) noexcept { - backend_pid = n.backend_pid; - }); - - // Trigger our notification receiver. - pqxx::perform([&cx, &channel] { - pqxx::work tx(cx); - tx.notify(channel); - tx.commit(); - }); - - int notifs{0}; - for (int i{0}; (i < 20) and (backend_pid == 0); ++i) - { - PQXX_CHECK_EQUAL(notifs, 0, "Got unexpected notifications."); - // Sleep for one second. I'm not proud of this, but how does one inject - // a change to the built-in clock in a static language? - pqxx::internal::wait_for(1000u); - notifs = cx.get_notifs(); - } - - PQXX_CHECK_EQUAL( - backend_pid, cx.backendpid(), - "Did not get our notification from our own backend."); - PQXX_CHECK_EQUAL(notifs, 1, "Got too many notifications."); -} - - -PQXX_REGISTER_TEST(test_004); -} // namespace From 2525c94dff6ab3ca1b3bc3b2f8607c1584e77393 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 30 Dec 2024 01:13:09 +0100 Subject: [PATCH 34/57] Remove some more redundant old-style tests. --- test/Makefile.am | 3 --- test/Makefile.in | 27 +++++---------------- test/test78.cxx | 44 ---------------------------------- test/test79.cxx | 49 -------------------------------------- test/test87.cxx | 62 ------------------------------------------------ 5 files changed, 6 insertions(+), 179 deletions(-) delete mode 100644 test/test78.cxx delete mode 100644 test/test79.cxx delete mode 100644 test/test87.cxx diff --git a/test/Makefile.am b/test/Makefile.am index aee6936b2..67280a457 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -69,11 +69,8 @@ runner_SOURCES = \ test75.cxx \ test76.cxx \ test77.cxx \ - test78.cxx \ - test79.cxx \ test82.cxx \ test84.cxx \ - test87.cxx \ test88.cxx \ test89.cxx \ test90.cxx \ diff --git a/test/Makefile.in b/test/Makefile.in index 678b6a532..9a3c5521d 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -135,9 +135,8 @@ am_runner_OBJECTS = test00.$(OBJEXT) test01.$(OBJEXT) test02.$(OBJEXT) \ test60.$(OBJEXT) test61.$(OBJEXT) test62.$(OBJEXT) \ test69.$(OBJEXT) test70.$(OBJEXT) test71.$(OBJEXT) \ test72.$(OBJEXT) test74.$(OBJEXT) test75.$(OBJEXT) \ - test76.$(OBJEXT) test77.$(OBJEXT) test78.$(OBJEXT) \ - test79.$(OBJEXT) test82.$(OBJEXT) test84.$(OBJEXT) \ - test87.$(OBJEXT) test88.$(OBJEXT) test89.$(OBJEXT) \ + test76.$(OBJEXT) test77.$(OBJEXT) test82.$(OBJEXT) \ + test84.$(OBJEXT) test88.$(OBJEXT) test89.$(OBJEXT) \ test90.$(OBJEXT) unit/test_array.$(OBJEXT) \ unit/test_blob.$(OBJEXT) unit/test_cancel_query.$(OBJEXT) \ unit/test_column.$(OBJEXT) unit/test_composite.$(OBJEXT) \ @@ -204,12 +203,10 @@ am__depfiles_remade = ./$(DEPDIR)/runner.Po ./$(DEPDIR)/test00.Po \ ./$(DEPDIR)/test70.Po ./$(DEPDIR)/test71.Po \ ./$(DEPDIR)/test72.Po ./$(DEPDIR)/test74.Po \ ./$(DEPDIR)/test75.Po ./$(DEPDIR)/test76.Po \ - ./$(DEPDIR)/test77.Po ./$(DEPDIR)/test78.Po \ - ./$(DEPDIR)/test79.Po ./$(DEPDIR)/test82.Po \ - ./$(DEPDIR)/test84.Po ./$(DEPDIR)/test87.Po \ - ./$(DEPDIR)/test88.Po ./$(DEPDIR)/test89.Po \ - ./$(DEPDIR)/test90.Po unit/$(DEPDIR)/test_array.Po \ - unit/$(DEPDIR)/test_blob.Po \ + ./$(DEPDIR)/test77.Po ./$(DEPDIR)/test82.Po \ + ./$(DEPDIR)/test84.Po ./$(DEPDIR)/test88.Po \ + ./$(DEPDIR)/test89.Po ./$(DEPDIR)/test90.Po \ + unit/$(DEPDIR)/test_array.Po unit/$(DEPDIR)/test_blob.Po \ unit/$(DEPDIR)/test_cancel_query.Po \ unit/$(DEPDIR)/test_column.Po unit/$(DEPDIR)/test_composite.Po \ unit/$(DEPDIR)/test_connection.Po \ @@ -497,11 +494,8 @@ runner_SOURCES = \ test75.cxx \ test76.cxx \ test77.cxx \ - test78.cxx \ - test79.cxx \ test82.cxx \ test84.cxx \ - test87.cxx \ test88.cxx \ test89.cxx \ test90.cxx \ @@ -730,11 +724,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test75.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test76.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test77.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test78.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test79.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test82.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test84.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test87.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test88.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test89.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test90.Po@am__quote@ # am--include-marker @@ -1074,11 +1065,8 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/test75.Po -rm -f ./$(DEPDIR)/test76.Po -rm -f ./$(DEPDIR)/test77.Po - -rm -f ./$(DEPDIR)/test78.Po - -rm -f ./$(DEPDIR)/test79.Po -rm -f ./$(DEPDIR)/test82.Po -rm -f ./$(DEPDIR)/test84.Po - -rm -f ./$(DEPDIR)/test87.Po -rm -f ./$(DEPDIR)/test88.Po -rm -f ./$(DEPDIR)/test89.Po -rm -f ./$(DEPDIR)/test90.Po @@ -1203,11 +1191,8 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/test75.Po -rm -f ./$(DEPDIR)/test76.Po -rm -f ./$(DEPDIR)/test77.Po - -rm -f ./$(DEPDIR)/test78.Po - -rm -f ./$(DEPDIR)/test79.Po -rm -f ./$(DEPDIR)/test82.Po -rm -f ./$(DEPDIR)/test84.Po - -rm -f ./$(DEPDIR)/test87.Po -rm -f ./$(DEPDIR)/test88.Po -rm -f ./$(DEPDIR)/test89.Po -rm -f ./$(DEPDIR)/test90.Po diff --git a/test/test78.cxx b/test/test78.cxx deleted file mode 100644 index 2ef1732be..000000000 --- a/test/test78.cxx +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include - -#include -#include - -#include "test_helpers.hxx" - - -// Example program for libpqxx. Send notification to self, using a -// notification name with unusal characters, and without polling. -namespace -{ -void test_078() -{ - pqxx::connection cx; - bool done{false}; - - std::string const channel{"my listener"}; - cx.listen(channel, [&done](pqxx::notification) noexcept { done = true; }); - - pqxx::perform([&cx, &channel] { - pqxx::nontransaction tx{cx}; - tx.notify(channel); - tx.commit(); - }); - - int notifs{0}; - for (int i{0}; (i < 20) and not done; ++i) - { - PQXX_CHECK_EQUAL(notifs, 0, "Got unexpected notifications."); - std::cout << "."; - notifs = cx.await_notification(); - } - std::cout << std::endl; - - PQXX_CHECK(done, "No notification received."); - PQXX_CHECK_EQUAL(notifs, 1, "Got unexpected number of notifications."); -} -} // namespace - - -PQXX_REGISTER_TEST(test_078); diff --git a/test/test79.cxx b/test/test79.cxx deleted file mode 100644 index 764114840..000000000 --- a/test/test79.cxx +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include -#include - -#include -#include - -#include "test_helpers.hxx" - - -// Example program for libpqxx. Test waiting for notification with timeout. -namespace -{ -void test_079() -{ - pqxx::connection cx; - - std::string const channel{"mylistener"}; - int backend_pid{0}; - - cx.listen(channel, [&backend_pid](pqxx::notification n) noexcept { - backend_pid = n.backend_pid; - }); - - // First see if the timeout really works: we're not expecting any notifs - int notifs{cx.await_notification(0, 1)}; - PQXX_CHECK_EQUAL(notifs, 0, "Got unexpected notification."); - - pqxx::perform([&cx, &channel] { - pqxx::work tx{cx}; - tx.notify(channel); - tx.commit(); - }); - - for (int i{0}; (i < 20) and (backend_pid == 0); ++i) - { - PQXX_CHECK_EQUAL(notifs, 0, "Got notifications, but no handler called."); - std::cout << "."; - notifs = cx.await_notification(1, 0); - } - std::cout << std::endl; - - PQXX_CHECK_EQUAL(backend_pid, cx.backendpid(), "Wrong backend."); - PQXX_CHECK_EQUAL(notifs, 1, "Got unexpected notifications."); -} -} // namespace - - -PQXX_REGISTER_TEST(test_079); diff --git a/test/test87.cxx b/test/test87.cxx deleted file mode 100644 index 628f2cbd4..000000000 --- a/test/test87.cxx +++ /dev/null @@ -1,62 +0,0 @@ -#include "pqxx/config-public-compiler.h" -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#include -#include - -#include "test_helpers.hxx" - - -// Test program for libpqxx. Send notification to self, and wait on the -// socket's connection for it to come in. In a simple situation you'd use -// connection::await_notification() for this, but that won't let you wait for -// multiple sockets. -namespace -{ -void test_087() -{ - pqxx::connection cx; - - std::string const channel{"my notification"}; - int backend_pid{0}; - - cx.listen(channel, [&backend_pid](pqxx::notification n) noexcept { - backend_pid = n.backend_pid; - }); - - pqxx::perform([&cx, &channel] { - pqxx::work tx{cx}; - tx.notify(channel); - tx.commit(); - }); - - int notifs{0}; - for (int i{0}; (i < 20) and (backend_pid == 0); ++i) - { - PQXX_CHECK_EQUAL(notifs, 0, "Got unexpected notifications."); - - std::cout << "."; - - pqxx::internal::wait_fd(cx.sock(), true, false); - notifs = cx.get_notifs(); - } - std::cout << std::endl; - - PQXX_CHECK_EQUAL( - backend_pid, cx.backendpid(), "Notification came from wrong backend."); - PQXX_CHECK_EQUAL(notifs, 1, "Got unexpected number of notifications."); -} -} // namespace - - -PQXX_REGISTER_TEST(test_087); From 719ac988a85775634dc179b4da8bac2f1b8c09dd Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 16:57:55 +0100 Subject: [PATCH 35/57] Retire `connection_base` type alias. This has been deprecated for the entire 7.x cycle. --- include/pqxx/connection.hxx | 4 ---- test/unit/test_stream_to.cxx | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index ed8a884cc..e3a68c8be 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -1341,10 +1341,6 @@ private: }; -/// @deprecated Old base class for connection. They are now the same class. -using connection_base = connection; - - /// An ongoing, non-blocking stepping stone to a connection. /** Use this when you want to create a connection to the database, but without * blocking your whole thread. It is only available on systems that have diff --git a/test/unit/test_stream_to.cxx b/test/unit/test_stream_to.cxx index 12e0eb2aa..91e52a247 100644 --- a/test/unit/test_stream_to.cxx +++ b/test/unit/test_stream_to.cxx @@ -306,7 +306,7 @@ void test_container_stream_to() tx.commit(); } -void test_variant_fold(pqxx::connection_base &connection) +void test_variant_fold(pqxx::connection &connection) { pqxx::work tx{connection}; auto inserter{pqxx::stream_to::table(tx, {"stream_to_test"})}; From 8799eab69962cac78ff2b5509c7208e29046a224 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 16:59:20 +0100 Subject: [PATCH 36/57] Tweaks. --- NEWS | 9 +++++++-- include/pqxx/util.hxx | 6 +++--- src/util.cxx | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index abf953e96..629af007b 100644 --- a/NEWS +++ b/NEWS @@ -1,9 +1,14 @@ 8.0.0 - C++20 is now the oldest C++ version that libpqxx supports. + - Retired `binarystring` and its headers. Use `blob` instead. + - Retired result row slicing. + - Retired `connection_base` type alias. Use `connection`. + - Assume compiler supports concepts. - Assume compiler supports integral conversions in `charconv`. - Assume compiler supports spans, ranges, and `cmp_less` etc. - - Retired `binarystring` and its headers. - - Retired result row slicing. + - Assume compiler supports `[[likely]]` & `[[unlikely]]`. + - Assume compiler supports `ssize()`. + - Assume compiler supports ISO-646 without needing `` header. 7.10.1 - Fix string conversion buffer budget for arrays containing nulls. (#921) - Remove `-fanalyzer` option again; gcc is still broken. diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index 57ac93225..9f1d27e4c 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -239,8 +239,8 @@ struct byte_char_traits : std::char_traits } /// Deliberately undefined: "guess" the length of an array of bytes. - /* This is nonsense: we can't determine the length of a random sequence of - * bytes. There is no terminating zero like there is for C strings. + /* This would be nonsense: we can't determine the length of a random sequence + * of bytes. There is no terminating zero like there is for C strings. * * But `std::char_traits` requires us to provide this function, so we * declare it without defining it. @@ -330,7 +330,7 @@ using bytes_view = std::conditional< * may have yours as a `std::string`, or a `std::vector`, or one of * many other types. In libpqxx we commend a container of `std::byte`. * - * For libpqxx to recognise your data as binary, it needs to be a + * For libpqxx to recognise your data as binary, we recommend using a * `pqxx::bytes`, or a `pqxx::bytes_view`; but any contiguous block of * `std::byte` should do. * diff --git a/src/util.cxx b/src/util.cxx index 1b7cc0735..57283c52f 100644 --- a/src/util.cxx +++ b/src/util.cxx @@ -125,9 +125,9 @@ constexpr int nibble(int c) noexcept return c - '0'; else if (c >= 'a' and c <= 'f') return ten + (c - 'a'); - else if (c >= 'A' and c <= 'F') + else [[unlikely]] if (c >= 'A' and c <= 'F') return ten + (c - 'A'); - else + else [[unlikely]] return -1; } } // namespace From 3fabf3e4129dbd7224975d3207f22c1052ffc321 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 17:09:11 +0100 Subject: [PATCH 37/57] Retired `pqxx::encrypt_password()`. Use `pqxx::connection::encrypt_password()` instead. --- NEWS | 1 + include/pqxx/connection.hxx | 17 ----------------- src/connection.cxx | 9 --------- 3 files changed, 1 insertion(+), 26 deletions(-) diff --git a/NEWS b/NEWS index 629af007b..bbc851456 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ - Retired `binarystring` and its headers. Use `blob` instead. - Retired result row slicing. - Retired `connection_base` type alias. Use `connection`. + - Retired `pqxx::encrypt_password()`. Use the ones in `pqxx::connection`. - Assume compiler supports concepts. - Assume compiler supports integral conversions in `charconv`. - Assume compiler supports spans, ranges, and `cmp_less` etc. diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index e3a68c8be..3c5e124f4 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -1498,22 +1498,5 @@ inline connection::connection(MAPPING const ¶ms) init(std::data(keys), std::data(values)); } #endif // PQXX_HAVE_CONCEPTS - - -/// Encrypt a password. @deprecated Use connection::encrypt_password instead. -[[nodiscard, - deprecated("Use connection::encrypt_password instead.")]] std::string - PQXX_LIBEXPORT - encrypt_password(char const user[], char const password[]); - -/// Encrypt password. @deprecated Use connection::encrypt_password instead. -[[nodiscard, - deprecated("Use connection::encrypt_password instead.")]] inline std::string -encrypt_password(zview user, zview password) -{ -#include "pqxx/internal/ignore-deprecated-pre.hxx" - return encrypt_password(user.c_str(), password.c_str()); -#include "pqxx/internal/ignore-deprecated-post.hxx" -} } // namespace pqxx #endif diff --git a/src/connection.cxx b/src/connection.cxx index c1a36424f..3dc24d45d 100644 --- a/src/connection.cxx +++ b/src/connection.cxx @@ -105,15 +105,6 @@ void PQXX_COLD PQXX_LIBEXPORT pqxx::internal::skip_init_ssl(int skips) noexcept } -std::string PQXX_COLD -pqxx::encrypt_password(char const user[], char const password[]) -{ - std::unique_ptr const p{ - PQencryptPassword(password, user), pqxx::internal::pq::pqfreemem}; - return {p.get()}; -} - - pqxx::connection::connection(connection &&rhs) : m_conn{rhs.m_conn}, m_notice_waiters{std::move(rhs.m_notice_waiters)}, From dbdbafe4535c6d465ab0271a58dee245f3b8cb9c Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 17:39:35 +0100 Subject: [PATCH 38/57] Retire `pqxx::prepare::dynamic_params`. Use `pqxx::params` instead. --- NEWS | 1 + .../pqxx/internal/statement_parameters.hxx | 50 ------------------- include/pqxx/params.hxx | 44 ---------------- 3 files changed, 1 insertion(+), 94 deletions(-) diff --git a/NEWS b/NEWS index bbc851456..24f202c2d 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ - Retired result row slicing. - Retired `connection_base` type alias. Use `connection`. - Retired `pqxx::encrypt_password()`. Use the ones in `pqxx::connection`. + - Retired `pqxx::prepare::dynamic_params`. Use `pqxx::params` instead. - Assume compiler supports concepts. - Assume compiler supports integral conversions in `charconv`. - Assume compiler supports spans, ranges, and `cmp_less` etc. diff --git a/include/pqxx/internal/statement_parameters.hxx b/include/pqxx/internal/statement_parameters.hxx index 3a44bb774..1e1b2319f 100644 --- a/include/pqxx/internal/statement_parameters.hxx +++ b/include/pqxx/internal/statement_parameters.hxx @@ -29,56 +29,6 @@ constexpr inline auto const iterator_identity{ [](decltype(*std::declval()) x) { return x; }}; -/// @deprecated Use @ref params instead. -template)> -class dynamic_params -{ -public: - /// Wrap a sequence of pointers or iterators. - constexpr dynamic_params(IT begin, IT end) : - m_begin(begin), m_end(end), m_accessor(iterator_identity) - {} - - /// Wrap a sequence of pointers or iterators. - /** This version takes an accessor callable. If you pass an accessor `acc`, - * then any parameter `p` will go into the statement's parameter list as - * `acc(p)`. - */ - constexpr dynamic_params(IT begin, IT end, ACCESSOR &acc) : - m_begin(begin), m_end(end), m_accessor(acc) - {} - - /// Wrap a container. - template - explicit constexpr dynamic_params(C &container) : - dynamic_params(std::begin(container), std::end(container)) - {} - - /// Wrap a container. - /** This version takes an accessor callable. If you pass an accessor `acc`, - * then any parameter `p` will go into the statement's parameter list as - * `acc(p)`. - */ - template - explicit constexpr dynamic_params(C &container, ACCESSOR &acc) : - dynamic_params(std::begin(container), std::end(container), acc) - {} - - constexpr IT begin() const noexcept { return m_begin; } - constexpr IT end() const noexcept { return m_end; } - - constexpr auto access(decltype(*std::declval()) value) const - -> decltype(std::declval()(value)) - { - return m_accessor(value); - } - -private: - IT const m_begin, m_end; - ACCESSOR m_accessor = iterator_identity; -}; - - /// Internal type: encode statement parameters. /** Compiles arguments for prepared statements and parameterised queries into * a format that can be passed into libpq. diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index f28d43c6c..6b685e7c9 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -109,13 +109,6 @@ public: /// Append a non-null binary parameter. void append(bytes &&) &; - /// Append all parameters from value. - template - void append(pqxx::internal::dynamic_params const &value) & - { - for (auto ¶m : value) append(value.access(param)); - } - void append(params const &value) &; void append(params &&value) &; @@ -275,41 +268,4 @@ private: std::array::digits10 + 3> m_buf; }; } // namespace pqxx - - -/// @deprecated The new @ref params class replaces all of this. -namespace pqxx::prepare -{ -/// @deprecated Use @ref params instead. -template -[[deprecated("Use the params class instead.")]] constexpr inline auto -make_dynamic_params(IT begin, IT end) -{ - return pqxx::internal::dynamic_params(begin, end); -} - - -/// @deprecated Use @ref params instead. -template -[[deprecated("Use the params class instead.")]] constexpr inline auto -make_dynamic_params(C const &container) -{ - using IT = typename C::const_iterator; -#include "pqxx/internal/ignore-deprecated-pre.hxx" - return pqxx::internal::dynamic_params{container}; -#include "pqxx/internal/ignore-deprecated-post.hxx" -} - - -/// @deprecated Use @ref params instead. -template -[[deprecated("Use the params class instead.")]] constexpr inline auto -make_dynamic_params(C &container, ACCESSOR accessor) -{ - using IT = decltype(std::begin(container)); -#include "pqxx/internal/ignore-deprecated-pre.hxx" - return pqxx::internal::dynamic_params{container, accessor}; -#include "pqxx/internal/ignore-deprecated-post.hxx" -} -} // namespace pqxx::prepare #endif From 837707380521676dce41c8f8aafbfd7def3b3341 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 17:49:02 +0100 Subject: [PATCH 39/57] Retire deprecated `stream_to` constructors. Use the factory functions instead. --- NEWS | 5 +++-- include/pqxx/stream_to.hxx | 29 ----------------------------- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/NEWS b/NEWS index 24f202c2d..20455c0b5 100644 --- a/NEWS +++ b/NEWS @@ -1,10 +1,11 @@ 8.0.0 - C++20 is now the oldest C++ version that libpqxx supports. - Retired `binarystring` and its headers. Use `blob` instead. - - Retired result row slicing. - Retired `connection_base` type alias. Use `connection`. - Retired `pqxx::encrypt_password()`. Use the ones in `pqxx::connection`. - - Retired `pqxx::prepare::dynamic_params`. Use `pqxx::params` instead. + - Retired `pqxx::prepare::dynamic_params`. Use `pqxx::params`. + - Retired deprecated `stream_to` constructors. Use factory functions. + - Retired result row slicing. - Assume compiler supports concepts. - Assume compiler supports integral conversions in `charconv`. - Assume compiler supports spans, ranges, and `cmp_less` etc. diff --git a/include/pqxx/stream_to.hxx b/include/pqxx/stream_to.hxx index 79abc5723..bde579f21 100644 --- a/include/pqxx/stream_to.hxx +++ b/include/pqxx/stream_to.hxx @@ -238,28 +238,6 @@ public: write_buffer(); } - /// Create a stream, without specifying columns. - /** @deprecated Use @ref table or @ref raw_table as a factory. - * - * Fields will be inserted in whatever order the columns have in the - * database. - * - * You'll probably want to specify the columns, so that the mapping between - * your data fields and the table is explicit in your code, and not hidden - * in an "implicit contract" between your code and your schema. - */ - [[deprecated("Use table() or raw_table() factory.")]] stream_to( - transaction_base &tx, std::string_view table_name) : - stream_to{tx, table_name, ""sv} - {} - - /// Create a stream, specifying column names as a container of strings. - /** @deprecated Use @ref table or @ref raw_table as a factory. - */ - template - [[deprecated("Use table() or raw_table() factory.")]] stream_to( - transaction_base &, std::string_view table_name, Columns const &columns); - private: /// Stream a pre-quoted table name and columns list. stream_to( @@ -458,12 +436,5 @@ private: constexpr static std::string_view s_classname{"stream_to"}; }; - - -template -inline stream_to::stream_to( - transaction_base &tx, std::string_view table_name, Columns const &columns) : - stream_to{tx, table_name, std::begin(columns), std::end(columns)} -{} } // namespace pqxx #endif From ee7ef8ccf334a386a7e6d87be15a33de24c4f7df Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 18:05:17 +0100 Subject: [PATCH 40/57] Retire deprecated binary unescaping/quoting. Use `unesc_bin()` and `quote(bytes_view)` instead. --- NEWS | 2 ++ include/pqxx/transaction_base.hxx | 35 ------------------------------- src/transaction_base.cxx | 6 ------ 3 files changed, 2 insertions(+), 41 deletions(-) diff --git a/NEWS b/NEWS index 20455c0b5..c0b6a3e88 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ - Retired `pqxx::encrypt_password()`. Use the ones in `pqxx::connection`. - Retired `pqxx::prepare::dynamic_params`. Use `pqxx::params`. - Retired deprecated `stream_to` constructors. Use factory functions. + - Retired `transaction_base::unesc_raw()`. Use `unesc_bin()`. + - Retired `transaction_base::quote_raw()`. Use `quote()` with `bytes_view`. - Retired result row slicing. - Assume compiler supports concepts. - Assume compiler supports integral conversions in `charconv`. diff --git a/include/pqxx/transaction_base.hxx b/include/pqxx/transaction_base.hxx index 312e78eed..4bec22bdf 100644 --- a/include/pqxx/transaction_base.hxx +++ b/include/pqxx/transaction_base.hxx @@ -213,36 +213,12 @@ public: return conn().esc_raw(std::forward(args)...); } - /// Unescape binary data, e.g. from a `bytea` field. - /** Takes a binary string as escaped by PostgreSQL, and returns a restored - * copy of the original binary data. - */ - [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string - unesc_raw(zview text) const - { -#include "pqxx/internal/ignore-deprecated-pre.hxx" - return conn().unesc_raw(text); -#include "pqxx/internal/ignore-deprecated-post.hxx" - } - /// Unescape binary data, e.g. from a `bytea` field. /** Takes a binary string as escaped by PostgreSQL, and returns a restored * copy of the original binary data. */ [[nodiscard]] bytes unesc_bin(zview text) { return conn().unesc_bin(text); } - /// Unescape binary data, e.g. from a `bytea` field. - /** Takes a binary string as escaped by PostgreSQL, and returns a restored - * copy of the original binary data. - */ - [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string - unesc_raw(char const *text) const - { -#include "pqxx/internal/ignore-deprecated-pre.hxx" - return conn().unesc_raw(text); -#include "pqxx/internal/ignore-deprecated-post.hxx" - } - /// Unescape binary data, e.g. from a `bytea` field. /** Takes a binary string as escaped by PostgreSQL, and returns a restored * copy of the original binary data. @@ -259,17 +235,6 @@ public: return conn().quote(t); } - /// Binary-escape and quote a binary string for use as an SQL constant. - [[deprecated("Use quote(pqxx::bytes_view).")]] std::string - quote_raw(unsigned char const bin[], std::size_t len) const - { - return quote(binary_cast(bin, len)); - } - - /// Binary-escape and quote a binary string for use as an SQL constant. - [[deprecated("Use quote(pqxx::bytes_view).")]] std::string - quote_raw(zview bin) const; - #if defined(PQXX_HAVE_CONCEPTS) /// Binary-escape and quote a binary string for use as an SQL constant. /** For binary data you can also just use @ref quote(data). */ diff --git a/src/transaction_base.cxx b/src/transaction_base.cxx index 3972d0a3c..51dfddd77 100644 --- a/src/transaction_base.cxx +++ b/src/transaction_base.cxx @@ -213,12 +213,6 @@ void pqxx::transaction_base::abort() } -std::string PQXX_COLD pqxx::transaction_base::quote_raw(zview bin) const -{ - return conn().quote(binary_cast(bin)); -} - - namespace { /// Guard command execution against clashes with pipelines and such. From ddda717c3a2f53681385ad463f655237490e616d Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 18:12:45 +0100 Subject: [PATCH 41/57] Split off deprecated `desc` further. --- include/pqxx/transaction_base.hxx | 8 +------- src/transaction_base.cxx | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/include/pqxx/transaction_base.hxx b/include/pqxx/transaction_base.hxx index 4bec22bdf..1d12f1557 100644 --- a/include/pqxx/transaction_base.hxx +++ b/include/pqxx/transaction_base.hxx @@ -298,7 +298,6 @@ public: /// Execute a command. /** * @param query Query or command to execute. - * @param desc Optional identifier for query, to help pinpoint SQL errors. * @return A result set describing the query's or command's result. */ [[deprecated("The desc parameter is going away.")]] @@ -316,12 +315,7 @@ public: * @param query Query or command to execute. * @return A result set describing the query's or command's result. */ - result exec(std::string_view query) - { -#include "pqxx/internal/ignore-deprecated-pre.hxx" - return exec(query, std::string_view{}); -#include "pqxx/internal/ignore-deprecated-post.hxx" - } + result exec(std::string_view query); /// Execute a command. /** diff --git a/src/transaction_base.cxx b/src/transaction_base.cxx index 51dfddd77..75dad4667 100644 --- a/src/transaction_base.cxx +++ b/src/transaction_base.cxx @@ -238,6 +238,31 @@ class PQXX_PRIVATE command : pqxx::transaction_focus }; } // namespace +pqxx::result +pqxx::transaction_base::exec(std::string_view query) +{ + check_pending_error(); + + command const cmd{*this, {}}; + + switch (m_status) + { + case status::active: break; + + case status::committed: + case status::aborted: + case status::in_doubt: + // TODO: Pass query. + throw usage_error{ + "Could not execute command: transaction is already closed."}; + + default: PQXX_UNREACHABLE; + } + + return direct_exec(query); +} + + pqxx::result pqxx::transaction_base::exec(std::string_view query, std::string_view desc) { From b1bcde795b325d74c83a1e3bd0fddd1c3cf5caca Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 18:52:07 +0100 Subject: [PATCH 42/57] Use `std::remove_cvref_t`. --- NEWS | 1 + include/pqxx/internal/conversions.hxx | 8 ++++---- include/pqxx/internal/stream_query.hxx | 2 +- include/pqxx/params.hxx | 2 +- include/pqxx/row.hxx | 2 +- include/pqxx/separated_list.hxx | 2 +- include/pqxx/strconv.hxx | 6 +++--- include/pqxx/stream_from.hxx | 2 +- include/pqxx/types.hxx | 13 +++++++------ include/pqxx/util.hxx | 10 +++++----- include/pqxx/zview.hxx | 4 ++-- src/params.cxx | 2 +- 12 files changed, 28 insertions(+), 26 deletions(-) diff --git a/NEWS b/NEWS index c0b6a3e88..f1b0f1e35 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ - Assume compiler supports concepts. - Assume compiler supports integral conversions in `charconv`. - Assume compiler supports spans, ranges, and `cmp_less` etc. + - Assume compiler supports `std::remove_cvref_t`. - Assume compiler supports `[[likely]]` & `[[unlikely]]`. - Assume compiler supports `ssize()`. - Assume compiler supports ISO-646 without needing `` header. diff --git a/include/pqxx/internal/conversions.hxx b/include/pqxx/internal/conversions.hxx index a5177fa5e..3f07a3db7 100644 --- a/include/pqxx/internal/conversions.hxx +++ b/include/pqxx/internal/conversions.hxx @@ -334,7 +334,7 @@ template struct nullness> return value.valueless_by_exception() or std::visit( [](auto const &i) noexcept { - return nullness>::is_null(i); + return nullness>::is_null(i); }, value); } @@ -357,7 +357,7 @@ template struct string_traits> { return std::visit( [begin, end](auto const &i) { - return string_traits>::into_buf(begin, end, i); + return string_traits>::into_buf(begin, end, i); }, value); } @@ -365,7 +365,7 @@ template struct string_traits> { return std::visit( [begin, end](auto const &i) { - return string_traits>::to_buf(begin, end, i); + return string_traits>::to_buf(begin, end, i); }, value); } @@ -1031,7 +1031,7 @@ namespace pqxx::internal template struct array_string_traits { private: - using elt_type = strip_t>; + using elt_type = std::remove_cvref_t>; using elt_traits = string_traits; static constexpr zview s_null{"NULL"}; diff --git a/include/pqxx/internal/stream_query.hxx b/include/pqxx/internal/stream_query.hxx index 2aa1f8964..3cd73699b 100644 --- a/include/pqxx/internal/stream_query.hxx +++ b/include/pqxx/internal/stream_query.hxx @@ -268,7 +268,7 @@ private: template TARGET parse_field(zview line, std::size_t &offset, char *&write) { - using field_type = strip_t; + using field_type = std::remove_cvref_t; using nullity = nullness; assert(offset <= std::size(line)); diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index 6b685e7c9..4ac35c50e 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -118,7 +118,7 @@ public: template void append(TYPE const &value) & { // TODO: Pool storage for multiple string conversions in one buffer? - if constexpr (nullness>::always_null) + if constexpr (nullness>::always_null) { ignore_unused(value); m_params.emplace_back(); diff --git a/include/pqxx/row.hxx b/include/pqxx/row.hxx index 8467beb36..abeb20b90 100644 --- a/include/pqxx/row.hxx +++ b/include/pqxx/row.hxx @@ -550,7 +550,7 @@ const_row_iterator::operator-(const_row_iterator const &i) const noexcept template inline void row::extract_value(Tuple &t) const { - using field_type = strip_t(t))>; + using field_type = std::remove_cvref_t(t))>; field const f{m_result, m_index, index}; std::get(t) = from_string(f); } diff --git a/include/pqxx/separated_list.hxx b/include/pqxx/separated_list.hxx index a76ef00c0..8e4d659eb 100644 --- a/include/pqxx/separated_list.hxx +++ b/include/pqxx/separated_list.hxx @@ -53,7 +53,7 @@ separated_list(std::string_view sep, ITER begin, ITER end, ACCESS access) return to_string(access(begin)); // From here on, we've got at least 2 elements -- meaning that we need sep. - using elt_type = strip_t; + using elt_type = std::remove_cvref_t; using traits = string_traits; std::size_t budget{0}; diff --git a/include/pqxx/strconv.hxx b/include/pqxx/strconv.hxx index 40bff6c1b..5aa71ab70 100644 --- a/include/pqxx/strconv.hxx +++ b/include/pqxx/strconv.hxx @@ -510,7 +510,7 @@ inline void into_string(TYPE const &value, std::string &out); template [[nodiscard]] inline constexpr bool is_null(TYPE const &value) noexcept { - return nullness>::is_null(value); + return nullness>::is_null(value); } @@ -521,7 +521,7 @@ template template [[nodiscard]] inline std::size_t size_buffer(TYPE const &...value) noexcept { - return (string_traits>::size_buffer(value) + ...); + return (string_traits>::size_buffer(value) + ...); } @@ -602,7 +602,7 @@ inline zview generic_to_buf(char *begin, char *end, TYPE const &value) */ template concept binary = std::ranges::contiguous_range and - std::is_same_v>, std::byte>; + std::is_same_v>, std::byte>; #endif //@} } // namespace pqxx diff --git a/include/pqxx/stream_from.hxx b/include/pqxx/stream_from.hxx index ab538fa68..09f927263 100644 --- a/include/pqxx/stream_from.hxx +++ b/include/pqxx/stream_from.hxx @@ -343,7 +343,7 @@ template inline stream_from &stream_from::operator>>(Tuple &t) template inline void stream_from::extract_value(Tuple &t) const { - using field_type = strip_t(t))>; + using field_type = std::remove_cvref_t(t))>; using nullity = nullness; assert(index < std::size(m_fields)); if constexpr (nullity::always_null) diff --git a/include/pqxx/types.hxx b/include/pqxx/types.hxx index a0f1f7d5e..a04892a01 100644 --- a/include/pqxx/types.hxx +++ b/include/pqxx/types.hxx @@ -17,6 +17,7 @@ #include #include #include +#include namespace pqxx @@ -70,10 +71,10 @@ enum class format : int /// Remove any constness, volatile, and reference-ness from a type. -/** @deprecated In C++20 we'll replace this with std::remove_cvref. +/** @deprecated Use `std::remove_cvref` instead. */ template -using strip_t = std::remove_cv_t>; +using strip_t = std::remove_cvref_t; #if defined(PQXX_HAVE_CONCEPTS) @@ -82,14 +83,14 @@ using strip_t = std::remove_cv_t>; * which we may or may not end up using for this. */ template -using value_type = strip_t()))>; +using value_type = std::remove_cvref_t()))>; #else // PQXX_HAVE_CONCEPTS /// The type of a container's elements. /** At the time of writing there's a similar thing in `std::experimental`, * which we may or may not end up using for this. */ template -using value_type = strip_t()))>; +using value_type = std::remove_cvref_t()))>; #endif // PQXX_HAVE_CONCEPTS @@ -97,12 +98,12 @@ using value_type = strip_t()))>; /// Concept: Any type that we can read as a string of `char`. template concept char_string = std::ranges::contiguous_range and - std::same_as>, char>; + std::same_as>, char>; /// Concept: Anything we can iterate to get things we can read as strings. template concept char_strings = - std::ranges::range and char_string>>; + std::ranges::range and char_string>>; /// Concept: Anything we might want to treat as binary data. template diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index 9f1d27e4c..721e92b45 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -347,7 +347,7 @@ bytes_view binary_cast(TYPE const &data) // C++20: Use std::as_bytes. return { reinterpret_cast( - const_cast const *>( + const_cast const *>( std::data(data))), std::size(data)}; } @@ -539,15 +539,15 @@ template using args_t = decltype(args_f(std::declval())); -/// Helper: Apply `strip_t` to each of a tuple type's component types. +/// Apply `std::remove_cvref_t` to each of a tuple type's component types. /** This function has no definition. It is not meant to be called, only to be * used to deduce the right types. */ template -std::tuple...> strip_types(std::tuple const &); +std::tuple...> strip_types(std::tuple const &); -/// Take a tuple type and apply @ref strip_t to its component types. +/// Take a tuple type and apply std::remove_cvref_t to its component types. template using strip_types_t = decltype(strip_types(std::declval())); @@ -591,7 +591,7 @@ error_string(int err_num, std::array &buffer) # else auto const err_result{strerror_r(err_num, std::data(buffer), BYTES)}; # endif - if constexpr (std::is_same_v, char *>) + if constexpr (std::is_same_v, char *>) { // GNU version of strerror_r; returns the error string, which may or may // not reside within buffer. diff --git a/include/pqxx/zview.hxx b/include/pqxx/zview.hxx index b40f95839..546a058a6 100644 --- a/include/pqxx/zview.hxx +++ b/include/pqxx/zview.hxx @@ -131,8 +131,8 @@ namespace pqxx::internal * support each of these individually. */ template -concept ZString = std::is_convertible_v, char const *> or - std::is_convertible_v, zview> or +concept ZString = std::is_convertible_v, char const *> or + std::is_convertible_v, zview> or std::is_convertible_v; } // namespace pqxx::internal #endif // PQXX_HAVE_CONCEPTS diff --git a/src/params.cxx b/src/params.cxx index 6ba77d887..aa8350967 100644 --- a/src/params.cxx +++ b/src/params.cxx @@ -93,7 +93,7 @@ pqxx::internal::c_params pqxx::params::make_c_params() const for (auto const ¶m : m_params) std::visit( [&p](auto const &value) { - using T = strip_t; + using T = std::remove_cvref_t; if constexpr (std::is_same_v) { From dc914df904e6cc90a70a559062835df17fa5ebc4 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 19:33:54 +0100 Subject: [PATCH 43/57] Retire more deprecated quoting/escaping. --- include/pqxx/connection.hxx | 38 ------------------------------------- src/connection.cxx | 38 ------------------------------------- 2 files changed, 76 deletions(-) diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index 3c5e124f4..87fc713ae 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -909,10 +909,6 @@ public: } #endif - /// Escape binary string for use as SQL string literal on this connection. - [[deprecated("Use std::byte for binary data.")]] std::string - esc_raw(unsigned char const bin[], std::size_t len) const; - /// Escape binary string for use as SQL string literal on this connection. /** You can also just use @ref esc with a binary string. */ [[nodiscard]] std::string esc_raw(bytes_view) const; @@ -1046,40 +1042,6 @@ public: */ [[nodiscard]] std::string esc_like(std::string_view text, char escape_char = '\\') const; - - /// Escape string for use as SQL string literal on this connection. - /** @warning This accepts a length, and it does not require a terminating - * zero byte. But if there is a zero byte, escaping stops there even if - * it's not at the end of the string! - */ - [[deprecated("Use std::string_view or pqxx:zview.")]] std::string - esc(char const text[], std::size_t maxlen) const - { - return esc(std::string_view{text, maxlen}); - } - - /// Unescape binary data, e.g. from a `bytea` field. - /** Takes a binary string as escaped by PostgreSQL, and returns a restored - * copy of the original binary data. - */ - [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string - unesc_raw(zview text) const - { -#include "pqxx/internal/ignore-deprecated-pre.hxx" - return unesc_raw(text.c_str()); -#include "pqxx/internal/ignore-deprecated-post.hxx" - } - - /// Unescape binary data, e.g. from a `bytea` field. - /** Takes a binary string as escaped by PostgreSQL, and returns a restored - * copy of the original binary data. - */ - [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string - unesc_raw(char const text[]) const; - - /// Escape and quote a string of binary data. - [[deprecated("Use quote(bytes_view).")]] std::string - quote_raw(unsigned char const bin[], std::size_t len) const; //@} /// Attempt to cancel the ongoing query, if any. diff --git a/src/connection.cxx b/src/connection.cxx index 3dc24d45d..539b337d4 100644 --- a/src/connection.cxx +++ b/src/connection.cxx @@ -963,50 +963,12 @@ std::string pqxx::connection::esc(std::string_view text) const } -std::string PQXX_COLD -pqxx::connection::esc_raw(unsigned char const bin[], std::size_t len) const -{ - return pqxx::internal::esc_bin(binary_cast(bin, len)); -} - - std::string pqxx::connection::esc_raw(bytes_view bin) const { return pqxx::internal::esc_bin(bin); } -std::string PQXX_COLD pqxx::connection::unesc_raw(char const text[]) const -{ - if (text[0] == '\\' and text[1] == 'x') - { - // Hex-escaped format. - std::string buf; - buf.resize(pqxx::internal::size_unesc_bin(std::strlen(text))); - pqxx::internal::unesc_bin( - std::string_view{text}, reinterpret_cast(buf.data())); - return buf; - } - else - { - // Legacy escape format. - // TODO: Remove legacy support. - std::size_t len{}; - auto bytes{reinterpret_cast(text)}; - std::unique_ptr const ptr{ - PQunescapeBytea(bytes, &len), pqxx::internal::pq::pqfreemem}; - return std::string{ptr.get(), ptr.get() + len}; - } -} - - -std::string PQXX_COLD -pqxx::connection::quote_raw(unsigned char const bin[], std::size_t len) const -{ - return internal::concat("'", esc_raw(binary_cast(bin, len)), "'::bytea"); -} - - std::string pqxx::connection::quote_raw(bytes_view bytes) const { return internal::concat("'", esc_raw(bytes), "'::bytea"); From bb433a8978e3d8a2db9482527cc7f004f48b21b6 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 19:43:00 +0100 Subject: [PATCH 44/57] Make deprecated `field` ctors public. These constructors have been documented as "do not use" in various ways for a long time. More generally, I'm settling on a deprecation horizon of 3 years. It's been so long since 7.0 came out that it's just not reasonable to keep everything that was deprecated since then. Instead, I'm now retiring things that were deprecated in 7.6. --- NEWS | 1 + include/pqxx/field.hxx | 13 ++++--------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index f1b0f1e35..b5b9055b4 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ - Retired `transaction_base::unesc_raw()`. Use `unesc_bin()`. - Retired `transaction_base::quote_raw()`. Use `quote()` with `bytes_view`. - Retired result row slicing. + - Deprecated `field` constructors are no longer publicly accessible. - Assume compiler supports concepts. - Assume compiler supports integral conversions in `charconv`. - Assume compiler supports spans, ranges, and `cmp_less` etc. diff --git a/include/pqxx/field.hxx b/include/pqxx/field.hxx index 84f645611..e1fe709c3 100644 --- a/include/pqxx/field.hxx +++ b/include/pqxx/field.hxx @@ -272,21 +272,16 @@ public: } //@} - /// Constructor. Do not call this yourself; libpqxx will do it for you. +protected: /** Create field as reference to a field in a result set. * @param r Row that this field is part of. * @param c Column number of this field. */ - [[deprecated( - "Do not construct fields yourself. Get them from the row.")]] field(row const &r, row_size_type c) noexcept; + field(row const &r, row_size_type c) noexcept; - /// Constructor. Do not call this yourself; libpqxx will do it for you. - [[deprecated( - "Do not construct fields yourself. Get them from the " - "row.")]] field() noexcept = default; + /// Constructor. + field() noexcept = default; - -protected: constexpr result const &home() const noexcept { return m_home; } constexpr result::size_type idx() const noexcept { return m_row; } constexpr row_size_type col() const noexcept { return m_col; } From 3615ba583ce221e4cd3946c788ae8ad213b5c417 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 19:52:23 +0100 Subject: [PATCH 45/57] Remove a pair of "ignore deprecations" headers. Not clear why those were there in the first place. --- include/pqxx/internal/result_iterator.hxx | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/pqxx/internal/result_iterator.hxx b/include/pqxx/internal/result_iterator.hxx index 874f3b2a3..d7e799a7d 100644 --- a/include/pqxx/internal/result_iterator.hxx +++ b/include/pqxx/internal/result_iterator.hxx @@ -40,7 +40,6 @@ public: using size_type = result_size_type; using difference_type = result_difference_type; -#include "pqxx/internal/ignore-deprecated-pre.hxx" /// Create an iterator, but in an unusable state. const_result_iterator() noexcept = default; /// Copy an iterator. @@ -50,7 +49,6 @@ public: /// Begin iterating a @ref row. const_result_iterator(row const &t) noexcept : row{t} {} -#include "pqxx/internal/ignore-deprecated-post.hxx" /** * @name Dereferencing operators From 1185e579adeaeb5061ca07826164fbc802dbad17 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 20:03:57 +0100 Subject: [PATCH 46/57] Remove unneeded ignore-deprecated includes. --- include/pqxx/internal/result_iterator.hxx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/pqxx/internal/result_iterator.hxx b/include/pqxx/internal/result_iterator.hxx index d7e799a7d..7b911b5ae 100644 --- a/include/pqxx/internal/result_iterator.hxx +++ b/include/pqxx/internal/result_iterator.hxx @@ -67,10 +67,8 @@ public: /// Dereference the iterator. [[nodiscard]] pointer operator->() const { return this; } -#include "pqxx/internal/ignore-deprecated-pre.hxx" /// Dereference the iterator. [[nodiscard]] reference operator*() const { return *this; } -#include "pqxx/internal/ignore-deprecated-post.hxx" //@} /** @@ -91,17 +89,13 @@ public: //@{ const_result_iterator &operator=(const_result_iterator const &rhs) { -#include "pqxx/internal/ignore-deprecated-pre.hxx" row::operator=(rhs); -#include "pqxx/internal/ignore-deprecated-post.hxx" return *this; } const_result_iterator &operator=(const_result_iterator &&rhs) { -#include "pqxx/internal/ignore-deprecated-pre.hxx" row::operator=(std::move(rhs)); -#include "pqxx/internal/ignore-deprecated-post.hxx" return *this; } From ee5b52266c7b352e4e250b2a2938d57f0d0dc3ec Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 20:11:43 +0100 Subject: [PATCH 47/57] Remove another unneeded ignore-deprecated. --- include/pqxx/row.hxx | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/pqxx/row.hxx b/include/pqxx/row.hxx index abeb20b90..4f0c53193 100644 --- a/include/pqxx/row.hxx +++ b/include/pqxx/row.hxx @@ -276,9 +276,7 @@ public: using difference_type = row_difference_type; using reference = field; -#include "pqxx/internal/ignore-deprecated-pre.hxx" const_row_iterator() noexcept = default; -#include "pqxx/internal/ignore-deprecated-post.hxx" const_row_iterator(row const &t, row_size_type c) noexcept : field{t.m_result, t.m_index, c} {} From 749ab4a2dd0c33b0357b3d1f09401e0b0e634cfc Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 21:25:30 +0100 Subject: [PATCH 48/57] Assume concepts support. --- cmake/pqxx_cxx_feature_checks.cmake | 4 --- config-tests/PQXX_HAVE_CONCEPTS.cxx | 11 -------- config/Makefile.in | 2 +- configitems | 1 - configure | 40 --------------------------- cxx_features.txt | 1 - include/pqxx/config.h.in | 3 -- include/pqxx/connection.hxx | 21 +------------- include/pqxx/internal/conversions.hxx | 2 -- include/pqxx/params.hxx | 4 --- include/pqxx/strconv.hxx | 2 -- include/pqxx/stream_to.hxx | 2 -- include/pqxx/transaction_base.hxx | 2 -- include/pqxx/types.hxx | 11 -------- include/pqxx/util.hxx | 17 +++--------- include/pqxx/zview.hxx | 2 -- pqxx_cxx_feature_checks.ac | 10 ------- test/unit/test_connection.cxx | 4 --- test/unit/test_escape.cxx | 6 ---- test/unit/test_prepared_statement.cxx | 2 -- test/unit/test_stream_to.cxx | 5 ---- 21 files changed, 6 insertions(+), 146 deletions(-) delete mode 100644 config-tests/PQXX_HAVE_CONCEPTS.cxx diff --git a/cmake/pqxx_cxx_feature_checks.cmake b/cmake/pqxx_cxx_feature_checks.cmake index bd01142c6..b6fbe7e36 100644 --- a/cmake/pqxx_cxx_feature_checks.cmake +++ b/cmake/pqxx_cxx_feature_checks.cmake @@ -7,10 +7,6 @@ try_compile( PQXX_HAVE_CHARCONV_FLOAT ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CHARCONV_FLOAT.cxx ) -try_compile( - PQXX_HAVE_CONCEPTS ${PROJECT_BINARY_DIR} - SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CONCEPTS.cxx -) try_compile( PQXX_HAVE_CXA_DEMANGLE ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_CXA_DEMANGLE.cxx diff --git a/config-tests/PQXX_HAVE_CONCEPTS.cxx b/config-tests/PQXX_HAVE_CONCEPTS.cxx deleted file mode 100644 index 2b028fd81..000000000 --- a/config-tests/PQXX_HAVE_CONCEPTS.cxx +++ /dev/null @@ -1,11 +0,0 @@ -// Feature check for 'PQXX_HAVE_CONCEPTS'. -// Generated by generate_cxx_checks.py. -#include -#if !defined(__cpp_concepts) -# error "No PQXX_HAVE_CONCEPTS: __cpp_concepts is not set." -#endif -#if !__cpp_concepts -# error "No PQXX_HAVE_CONCEPTS: __cpp_concepts is false." -#endif - -int main() {} diff --git a/config/Makefile.in b/config/Makefile.in index 9b1454986..51586bda2 100644 --- a/config/Makefile.in +++ b/config/Makefile.in @@ -124,7 +124,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 depcomp install-sh ltmain.sh missing mkinstalldirs + config.sub install-sh ltmain.sh missing mkinstalldirs DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ diff --git a/configitems b/configitems index e4f653b8a..8208cd679 100644 --- a/configitems +++ b/configitems @@ -6,7 +6,6 @@ PACKAGE_TARNAME internal autotools PACKAGE_VERSION internal autotools PQXX_HAVE_ASSUME public compiler PQXX_HAVE_CHARCONV_FLOAT internal compiler -PQXX_HAVE_CONCEPTS public compiler PQXX_HAVE_CXA_DEMANGLE internal compiler PQXX_HAVE_GCC_PURE public compiler PQXX_HAVE_GCC_VISIBILITY public compiler diff --git a/configure b/configure index 2e7b96cd1..2e4d1dd41 100755 --- a/configure +++ b/configure @@ -17324,46 +17324,6 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_CHARCONV_FLOAT" >&5 printf "%s\n" "$PQXX_HAVE_CHARCONV_FLOAT" >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_CONCEPTS" >&5 -printf %s "checking PQXX_HAVE_CONCEPTS... " >&6; } -PQXX_HAVE_CONCEPTS=yes -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -// Feature check for 'PQXX_HAVE_CONCEPTS'. - -// Generated by generate_cxx_checks.py. - -#include - -#if !defined(__cpp_concepts) - -# error "No PQXX_HAVE_CONCEPTS: __cpp_concepts is not set." - -#endif - -#if !__cpp_concepts - -# error "No PQXX_HAVE_CONCEPTS: __cpp_concepts is false." - -#endif - - - -int main() {} - - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO" -then : - -printf "%s\n" "#define PQXX_HAVE_CONCEPTS 1" >>confdefs.h - -else $as_nop - PQXX_HAVE_CONCEPTS=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_CONCEPTS" >&5 -printf "%s\n" "$PQXX_HAVE_CONCEPTS" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_CXA_DEMANGLE" >&5 printf %s "checking PQXX_HAVE_CXA_DEMANGLE... " >&6; } PQXX_HAVE_CXA_DEMANGLE=yes diff --git a/cxx_features.txt b/cxx_features.txt index 464415362..3b722f97e 100644 --- a/cxx_features.txt +++ b/cxx_features.txt @@ -12,6 +12,5 @@ # Remember to enter each of these in configitems as well, or they won't # end up in the actual configuration headers. -PQXX_HAVE_CONCEPTS __cpp_concepts PQXX_HAVE_MULTIDIM __cpp_multidimensional_subscript PQXX_HAVE_SOURCE_LOCATION __cpp_lib_source_location diff --git a/include/pqxx/config.h.in b/include/pqxx/config.h.in index b07152fbc..b79862a6b 100644 --- a/include/pqxx/config.h.in +++ b/include/pqxx/config.h.in @@ -63,9 +63,6 @@ /* Define if this feature is available. */ #undef PQXX_HAVE_CHARCONV_FLOAT -/* Define if this feature is available. */ -#undef PQXX_HAVE_CONCEPTS - /* Define if this feature is available. */ #undef PQXX_HAVE_CXA_DEMANGLE diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index 87fc713ae..3c4c33e4c 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -77,7 +77,6 @@ namespace pqxx::internal { class sql_cursor; -#if defined(PQXX_HAVE_CONCEPTS) /// Concept: T is a range of pairs of zero-terminated strings. template concept ZKey_ZValues = std::ranges::input_range and requires(T t) { @@ -85,7 +84,6 @@ concept ZKey_ZValues = std::ranges::input_range and requires(T t) { { std::get<0>(*std::cbegin(t)) } -> ZString; { std::get<1>(*std::cbegin(t)) } -> ZString; } and std::tuple_size_v::value_type> == 2; -#endif // PQXX_HAVE_CONCEPTS /// Control OpenSSL/crypto library initialisation. @@ -298,12 +296,8 @@ public: */ connection(connection &&rhs); -#if defined(PQXX_HAVE_CONCEPTS) /// Connect to a database, passing options as a range of key/value pairs. - /** @warning Experimental. Requires C++20 "concepts" support. Define - * `PQXX_HAVE_CONCEPTS` to enable it. - * - * There's no need to escape the parameter values. + /** There's no need to escape the parameter values. * * See the PostgreSQL libpq documentation for the full list of possible * options: @@ -317,7 +311,6 @@ public: */ template inline connection(MAPPING const ¶ms); -#endif // PQXX_HAVE_CONCEPTS ~connection() { @@ -868,16 +861,13 @@ public: */ [[nodiscard]] std::string esc(std::string_view text) const; -#if defined(PQXX_HAVE_CONCEPTS) /// Escape binary string for use as SQL string literal on this connection. /** This is identical to `esc_raw(data)`. */ template [[nodiscard]] std::string esc(DATA const &data) const { return esc_raw(data); } -#endif -#if defined(PQXX_HAVE_CONCEPTS) /// Escape binary string for use as SQL string literal, into `buffer`. /** Use this variant when you want to re-use the same buffer across multiple * calls. If that's not the case, or convenience and simplicity are more @@ -907,7 +897,6 @@ public: internal::esc_bin(view, out); return zview{out, needed - 1}; } -#endif /// Escape binary string for use as SQL string literal on this connection. /** You can also just use @ref esc with a binary string. */ @@ -917,7 +906,6 @@ public: /** You can also just use @ref esc with a binary string. */ [[nodiscard]] std::string esc_raw(bytes_view, std::span buffer) const; -#if defined(PQXX_HAVE_CONCEPTS) /// Escape binary string for use as SQL string literal on this connection. /** You can also just use @ref esc with a binary string. */ template @@ -925,16 +913,13 @@ public: { return esc_raw(bytes_view{std::data(data), std::size(data)}); } -#endif -#if defined(PQXX_HAVE_CONCEPTS) /// Escape binary string for use as SQL string literal, into `buffer`. template [[nodiscard]] zview esc_raw(DATA const &data, std::span buffer) const { return this->esc(binary_cast(data), buffer); } -#endif // TODO: Make "into buffer" variant to eliminate a string allocation. /// Unescape binary data, e.g. from a `bytea` field. @@ -956,7 +941,6 @@ public: /// Escape and quote a string of binary data. std::string quote_raw(bytes_view) const; -#if defined(PQXX_HAVE_CONCEPTS) /// Escape and quote a string of binary data. /** You can also just use @ref quote with binary data. */ template @@ -964,7 +948,6 @@ public: { return quote_raw(bytes_view{std::data(data), std::size(data)}); } -#endif // TODO: Make "into buffer" variant to eliminate a string allocation. /// Escape and quote an SQL identifier for use in a query. @@ -1437,7 +1420,6 @@ inline std::string connection::quote_columns(STRINGS const &columns) const } -#if defined(PQXX_HAVE_CONCEPTS) template inline connection::connection(MAPPING const ¶ms) { @@ -1459,6 +1441,5 @@ inline connection::connection(MAPPING const ¶ms) values.push_back(nullptr); init(std::data(keys), std::data(values)); } -#endif // PQXX_HAVE_CONCEPTS } // namespace pqxx #endif diff --git a/include/pqxx/internal/conversions.hxx b/include/pqxx/internal/conversions.hxx index 3f07a3db7..89a84f89b 100644 --- a/include/pqxx/internal/conversions.hxx +++ b/include/pqxx/internal/conversions.hxx @@ -895,7 +895,6 @@ template<> struct nullness : no_null {}; -#if defined(PQXX_HAVE_CONCEPTS) template struct nullness : no_null {}; @@ -940,7 +939,6 @@ template struct string_traits return buf; } }; -#endif // PQXX_HAVE_CONCEPTS template<> struct string_traits diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index 4ac35c50e..5212cd6a8 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -95,7 +95,6 @@ public: */ void append(bytes const &) &; -#if defined(PQXX_HAVE_CONCEPTS) /// Append a non-null binary parameter. /** The `data` object must stay in place and unchanged, for as long as the * `params` remains active. @@ -104,7 +103,6 @@ public: { append(bytes_view{std::data(data), std::size(data)}); } -#endif // PQXX_HAVE_CONCEPTS /// Append a non-null binary parameter. void append(bytes &&) &; @@ -136,10 +134,8 @@ public: /// Append all elements of `range` as parameters. template void append_multi(RANGE const &range) & { -#if defined(PQXX_HAVE_CONCEPTS) if constexpr (std::ranges::sized_range) reserve(std::size(*this) + std::size(range)); -#endif for (auto &value : range) append(value); } diff --git a/include/pqxx/strconv.hxx b/include/pqxx/strconv.hxx index 5aa71ab70..9781a6dcb 100644 --- a/include/pqxx/strconv.hxx +++ b/include/pqxx/strconv.hxx @@ -592,7 +592,6 @@ inline zview generic_to_buf(char *begin, char *end, TYPE const &value) } -#if defined(PQXX_HAVE_CONCEPTS) /// Concept: Binary string, akin to @c std::string for binary data. /** Any type that satisfies this concept can represent an SQL BYTEA value. * @@ -603,7 +602,6 @@ inline zview generic_to_buf(char *begin, char *end, TYPE const &value) template concept binary = std::ranges::contiguous_range and std::is_same_v>, std::byte>; -#endif //@} } // namespace pqxx diff --git a/include/pqxx/stream_to.hxx b/include/pqxx/stream_to.hxx index bde579f21..37e86c27f 100644 --- a/include/pqxx/stream_to.hxx +++ b/include/pqxx/stream_to.hxx @@ -125,7 +125,6 @@ public: return raw_table(tx, cx.quote_table(path), cx.quote_columns(columns)); } -#if defined(PQXX_HAVE_CONCEPTS) /// Create a `stream_to` writing to a named table and columns. /** Use this version to stream data to a table, when the list of columns is * not known at compile time. @@ -157,7 +156,6 @@ public: { return stream_to::raw_table(tx, path, tx.conn().quote_columns(columns)); } -#endif // PQXX_HAVE_CONCEPTS explicit stream_to(stream_to &&other) : // (This first step only moves the transaction_focus base-class diff --git a/include/pqxx/transaction_base.hxx b/include/pqxx/transaction_base.hxx index 1d12f1557..19942e01e 100644 --- a/include/pqxx/transaction_base.hxx +++ b/include/pqxx/transaction_base.hxx @@ -235,7 +235,6 @@ public: return conn().quote(t); } -#if defined(PQXX_HAVE_CONCEPTS) /// Binary-escape and quote a binary string for use as an SQL constant. /** For binary data you can also just use @ref quote(data). */ template @@ -243,7 +242,6 @@ public: { return conn().quote_raw(data); } -#endif /// Escape an SQL identifier for use in a query. [[nodiscard]] std::string quote_name(std::string_view identifier) const diff --git a/include/pqxx/types.hxx b/include/pqxx/types.hxx index a04892a01..674b9ed83 100644 --- a/include/pqxx/types.hxx +++ b/include/pqxx/types.hxx @@ -77,24 +77,14 @@ template using strip_t = std::remove_cvref_t; -#if defined(PQXX_HAVE_CONCEPTS) /// The type of a container's elements. /** At the time of writing there's a similar thing in `std::experimental`, * which we may or may not end up using for this. */ template using value_type = std::remove_cvref_t()))>; -#else // PQXX_HAVE_CONCEPTS -/// The type of a container's elements. -/** At the time of writing there's a similar thing in `std::experimental`, - * which we may or may not end up using for this. - */ -template -using value_type = std::remove_cvref_t()))>; -#endif // PQXX_HAVE_CONCEPTS -#if defined(PQXX_HAVE_CONCEPTS) /// Concept: Any type that we can read as a string of `char`. template concept char_string = std::ranges::contiguous_range and @@ -109,7 +99,6 @@ concept char_strings = template concept potential_binary = std::ranges::contiguous_range and (sizeof(value_type) == 1); -#endif // PQXX_HAVE_CONCEPTS /// Marker for @ref stream_from constructors: "stream from table." diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index 721e92b45..efb36f59f 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -214,12 +214,6 @@ struct PQXX_LIBEXPORT thread_safety_model [[nodiscard]] PQXX_LIBEXPORT thread_safety_model describe_thread_safety(); -#if defined(PQXX_HAVE_CONCEPTS) -# define PQXX_POTENTIAL_BINARY_ARG pqxx::potential_binary -#else -# define PQXX_POTENTIAL_BINARY_ARG typename -#endif - /// Custom `std::char_trast` if the compiler does not provide one. /** Needed if the standard library lacks a generic implementation or a * specialisation for std::byte. They aren't strictly required to provide @@ -340,7 +334,7 @@ using bytes_view = std::conditional< * @warning You must keep the storage holding the actual data alive for as * long as you might use this function's return value. */ -template +template bytes_view binary_cast(TYPE const &data) { static_assert(sizeof(value_type) == 1); @@ -353,13 +347,10 @@ bytes_view binary_cast(TYPE const &data) } -#if defined(PQXX_HAVE_CONCEPTS) +/// A type one byte in size. template concept char_sized = (sizeof(CHAR) == 1); -# define PQXX_CHAR_SIZED_ARG char_sized -#else -# define PQXX_CHAR_SIZED_ARG typename -#endif + /// Construct a type that libpqxx will recognise as binary. /** Takes a data pointer and a size, without being too strict about their @@ -368,7 +359,7 @@ concept char_sized = (sizeof(CHAR) == 1); * This makes it a little easier to turn binary data, in whatever form you * happen to have it, into binary data as libpqxx understands it. */ -template +template bytes_view binary_cast(CHAR const *data, SIZE size) { static_assert(sizeof(CHAR) == 1); diff --git a/include/pqxx/zview.hxx b/include/pqxx/zview.hxx index 546a058a6..de1110521 100644 --- a/include/pqxx/zview.hxx +++ b/include/pqxx/zview.hxx @@ -114,7 +114,6 @@ constexpr zview operator"" _zv(char const str[], std::size_t len) noexcept } // namespace pqxx -#if defined(PQXX_HAVE_CONCEPTS) /// A zview is a view. template<> inline constexpr bool std::ranges::enable_view{true}; @@ -135,7 +134,6 @@ concept ZString = std::is_convertible_v, char const *> or std::is_convertible_v, zview> or std::is_convertible_v; } // namespace pqxx::internal -#endif // PQXX_HAVE_CONCEPTS namespace pqxx::internal diff --git a/pqxx_cxx_feature_checks.ac b/pqxx_cxx_feature_checks.ac index 540d4e4ba..4ffe172a3 100644 --- a/pqxx_cxx_feature_checks.ac +++ b/pqxx_cxx_feature_checks.ac @@ -19,16 +19,6 @@ AC_COMPILE_IFELSE( [Define if this feature is available.]), PQXX_HAVE_CHARCONV_FLOAT=no) AC_MSG_RESULT($PQXX_HAVE_CHARCONV_FLOAT) -AC_MSG_CHECKING([PQXX_HAVE_CONCEPTS]) -PQXX_HAVE_CONCEPTS=yes -AC_COMPILE_IFELSE( - [read_test(PQXX_HAVE_CONCEPTS.cxx)], - AC_DEFINE( - [PQXX_HAVE_CONCEPTS], - 1, - [Define if this feature is available.]), - PQXX_HAVE_CONCEPTS=no) -AC_MSG_RESULT($PQXX_HAVE_CONCEPTS) AC_MSG_CHECKING([PQXX_HAVE_CXA_DEMANGLE]) PQXX_HAVE_CXA_DEMANGLE=yes AC_COMPILE_IFELSE( diff --git a/test/unit/test_connection.cxx b/test/unit/test_connection.cxx index 4a88e8efb..e9a52809d 100644 --- a/test/unit/test_connection.cxx +++ b/test/unit/test_connection.cxx @@ -105,7 +105,6 @@ void test_connection_string() } -#if defined(PQXX_HAVE_CONCEPTS) template std::size_t length(STR const &str) { return std::size(str); @@ -116,12 +115,10 @@ std::size_t length(char const str[]) { return std::strlen(str); } -#endif // PQXX_HAVE_CONCEPTS template void test_params_type() { -#if defined(PQXX_HAVE_CONCEPTS) using item_t = std::remove_reference_t< decltype(*std::declval>())>; using key_t = decltype(std::get<0>(std::declval())); @@ -160,7 +157,6 @@ template void test_params_type() "Could not find value for '" + std::string{value} + "' in connection string: " + connstr); } -#endif // PQXX_HAVE_CONCEPTS } diff --git a/test/unit/test_escape.cxx b/test/unit/test_escape.cxx index d88570ce4..1966f48f3 100644 --- a/test/unit/test_escape.cxx +++ b/test/unit/test_escape.cxx @@ -157,7 +157,6 @@ void test_escaping() void test_esc_escapes_into_buffer() { -#if defined(PQXX_HAVE_CONCEPTS) pqxx::connection cx; pqxx::work tx{cx}; @@ -171,13 +170,11 @@ void test_esc_escapes_into_buffer() pqxx::bytes const data{std::byte{0x22}, std::byte{0x43}}; auto escaped_data(tx.esc(data, buffer)); PQXX_CHECK_EQUAL(escaped_data, "\\x2243", "Binary data escaped wrong."); -#endif } void test_esc_accepts_various_types() { -#if defined(PQXX_HAVE_CONCEPTS) pqxx::connection cx; pqxx::work tx{cx}; @@ -191,13 +188,11 @@ void test_esc_accepts_various_types() std::vector const data{std::byte{0x23}, std::byte{0x44}}; auto escaped_data(tx.esc(data, buffer)); PQXX_CHECK_EQUAL(escaped_data, "\\x2344", "Binary data escaped wrong."); -#endif } void test_binary_esc_checks_buffer_length() { -#if defined(PQXX_HAVE_CONCEPTS) pqxx::connection cx; pqxx::work tx{cx}; @@ -216,7 +211,6 @@ void test_binary_esc_checks_buffer_length() PQXX_CHECK_THROWS( pqxx::ignore_unused(tx.esc(bin, buf)), pqxx::range_error, "Didn't get expected exception from escape overrun."); -#endif } diff --git a/test/unit/test_prepared_statement.cxx b/test/unit/test_prepared_statement.cxx index 9d435e4ea..54596a54a 100644 --- a/test/unit/test_prepared_statement.cxx +++ b/test/unit/test_prepared_statement.cxx @@ -216,7 +216,6 @@ void test_binary() input, "Binary string as shared_ptr-to-optional went wrong."); } -#if defined(PQXX_HAVE_CONCEPTS) // By the way, it doesn't have to be a pqxx::bytes. Any contiguous range // will do. { @@ -228,7 +227,6 @@ void test_binary() PQXX_CHECK_EQUAL(static_cast(oval[0]), int('x'), "Wrong data."); PQXX_CHECK_EQUAL(static_cast(oval[1]), int('v'), "Wrong data."); } -#endif } diff --git a/test/unit/test_stream_to.cxx b/test/unit/test_stream_to.cxx index 91e52a247..20efef55f 100644 --- a/test/unit/test_stream_to.cxx +++ b/test/unit/test_stream_to.cxx @@ -406,12 +406,7 @@ void test_stream_to_factory_with_dynamic_columns() tx.exec("CREATE TEMP TABLE pqxx_stream_to(a integer, b varchar)").no_rows(); std::vector columns{"a", "b"}; -#if defined(PQXX_HAVE_CONCEPTS) auto stream{pqxx::stream_to::table(tx, {"pqxx_stream_to"}, columns)}; -#else - auto stream{pqxx::stream_to::raw_table( - tx, cx.quote_table({"pqxx_stream_to"}), cx.quote_columns(columns))}; -#endif stream.write_values(4, "four"); stream.complete(); From e606cceedfd925cb7290d32841092a08c3fe49e1 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 21:29:40 +0100 Subject: [PATCH 49/57] Format. --- include/pqxx/internal/conversions.hxx | 6 ++++-- include/pqxx/row.hxx | 5 +---- include/pqxx/strconv.hxx | 5 +++-- include/pqxx/types.hxx | 15 ++++++++------- include/pqxx/util.hxx | 9 +++++---- include/pqxx/zview.hxx | 7 ++++--- src/transaction_base.cxx | 3 +-- 7 files changed, 26 insertions(+), 24 deletions(-) diff --git a/include/pqxx/internal/conversions.hxx b/include/pqxx/internal/conversions.hxx index 89a84f89b..844f81cbe 100644 --- a/include/pqxx/internal/conversions.hxx +++ b/include/pqxx/internal/conversions.hxx @@ -357,7 +357,8 @@ template struct string_traits> { return std::visit( [begin, end](auto const &i) { - return string_traits>::into_buf(begin, end, i); + return string_traits>::into_buf( + begin, end, i); }, value); } @@ -365,7 +366,8 @@ template struct string_traits> { return std::visit( [begin, end](auto const &i) { - return string_traits>::to_buf(begin, end, i); + return string_traits>::to_buf( + begin, end, i); }, value); } diff --git a/include/pqxx/row.hxx b/include/pqxx/row.hxx index 4f0c53193..d8ac841f2 100644 --- a/include/pqxx/row.hxx +++ b/include/pqxx/row.hxx @@ -103,10 +103,7 @@ public: */ reference at(zview col_name) const; - [[nodiscard]] constexpr size_type size() const noexcept - { - return m_end; - } + [[nodiscard]] constexpr size_type size() const noexcept { return m_end; } /// Row number, assuming this is a real row and not end()/rend(). [[nodiscard]] constexpr result::size_type rownumber() const noexcept diff --git a/include/pqxx/strconv.hxx b/include/pqxx/strconv.hxx index 9781a6dcb..3034c517e 100644 --- a/include/pqxx/strconv.hxx +++ b/include/pqxx/strconv.hxx @@ -600,8 +600,9 @@ inline zview generic_to_buf(char *begin, char *end, TYPE const &value) * we can reference them by a pointer. */ template -concept binary = std::ranges::contiguous_range and - std::is_same_v>, std::byte>; +concept binary = + std::ranges::contiguous_range and + std::is_same_v>, std::byte>; //@} } // namespace pqxx diff --git a/include/pqxx/types.hxx b/include/pqxx/types.hxx index 674b9ed83..28185fb36 100644 --- a/include/pqxx/types.hxx +++ b/include/pqxx/types.hxx @@ -73,8 +73,7 @@ enum class format : int /// Remove any constness, volatile, and reference-ness from a type. /** @deprecated Use `std::remove_cvref` instead. */ -template -using strip_t = std::remove_cvref_t; +template using strip_t = std::remove_cvref_t; /// The type of a container's elements. @@ -82,18 +81,20 @@ using strip_t = std::remove_cvref_t; * which we may or may not end up using for this. */ template -using value_type = std::remove_cvref_t()))>; +using value_type = + std::remove_cvref_t()))>; /// Concept: Any type that we can read as a string of `char`. template -concept char_string = std::ranges::contiguous_range and - std::same_as>, char>; +concept char_string = + std::ranges::contiguous_range and + std::same_as>, char>; /// Concept: Anything we can iterate to get things we can read as strings. template -concept char_strings = - std::ranges::range and char_string>>; +concept char_strings = std::ranges::range and + char_string>>; /// Concept: Anything we might want to treat as binary data. template diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index efb36f59f..7cb17e62e 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -334,8 +334,7 @@ using bytes_view = std::conditional< * @warning You must keep the storage holding the actual data alive for as * long as you might use this function's return value. */ -template -bytes_view binary_cast(TYPE const &data) +template bytes_view binary_cast(TYPE const &data) { static_assert(sizeof(value_type) == 1); // C++20: Use std::as_bytes. @@ -535,7 +534,8 @@ using args_t = decltype(args_f(std::declval())); * used to deduce the right types. */ template -std::tuple...> strip_types(std::tuple const &); +std::tuple...> +strip_types(std::tuple const &); /// Take a tuple type and apply std::remove_cvref_t to its component types. @@ -582,7 +582,8 @@ error_string(int err_num, std::array &buffer) # else auto const err_result{strerror_r(err_num, std::data(buffer), BYTES)}; # endif - if constexpr (std::is_same_v, char *>) + if constexpr (std::is_same_v< + std::remove_cvref_t, char *>) { // GNU version of strerror_r; returns the error string, which may or may // not reside within buffer. diff --git a/include/pqxx/zview.hxx b/include/pqxx/zview.hxx index de1110521..94083f686 100644 --- a/include/pqxx/zview.hxx +++ b/include/pqxx/zview.hxx @@ -130,9 +130,10 @@ namespace pqxx::internal * support each of these individually. */ template -concept ZString = std::is_convertible_v, char const *> or - std::is_convertible_v, zview> or - std::is_convertible_v; +concept ZString = + std::is_convertible_v, char const *> or + std::is_convertible_v, zview> or + std::is_convertible_v; } // namespace pqxx::internal diff --git a/src/transaction_base.cxx b/src/transaction_base.cxx index 75dad4667..c82c8162a 100644 --- a/src/transaction_base.cxx +++ b/src/transaction_base.cxx @@ -238,8 +238,7 @@ class PQXX_PRIVATE command : pqxx::transaction_focus }; } // namespace -pqxx::result -pqxx::transaction_base::exec(std::string_view query) +pqxx::result pqxx::transaction_base::exec(std::string_view query) { check_pending_error(); From 9e4eb644df10aa6440633056e1dd5922716ec8d4 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 21:36:02 +0100 Subject: [PATCH 50/57] Cosmetic. --- include/pqxx/strconv.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pqxx/strconv.hxx b/include/pqxx/strconv.hxx index 3034c517e..590b58c59 100644 --- a/include/pqxx/strconv.hxx +++ b/include/pqxx/strconv.hxx @@ -599,7 +599,7 @@ inline zview generic_to_buf(char *begin, char *end, TYPE const &value) * is a @c std::byte, and they must all be laid out contiguously in memory so * we can reference them by a pointer. */ -template +template concept binary = std::ranges::contiguous_range and std::is_same_v>, std::byte>; From eaf8d8811ce1d88556a2dc94014a57d9b9c13bfc Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 22:35:30 +0100 Subject: [PATCH 51/57] Work through some C++20 notes. --- include/pqxx/connection.hxx | 2 -- include/pqxx/doc/binary-data.md | 4 ++-- include/pqxx/internal/conversions.hxx | 4 ++-- include/pqxx/params.hxx | 1 - include/pqxx/prepared_statement.hxx | 4 ++-- src/connection.cxx | 8 ++++++-- src/encodings.cxx | 11 ++++------- src/sql_cursor.cxx | 1 - src/strconv.cxx | 6 +++--- test/unit/test_range.cxx | 7 +------ tools/generate_cxx_checks.py | 2 +- 11 files changed, 21 insertions(+), 29 deletions(-) diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index 3c4c33e4c..27a77a673 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -1058,8 +1058,6 @@ public: */ void set_verbosity(error_verbosity verbosity) & noexcept; - // C++20: Use std::callable. - /// Set a notice handler to the connection. /** When a notice comes in (a warning or error message), the connection or * result object on which it happens will call the notice handler, passing diff --git a/include/pqxx/doc/binary-data.md b/include/pqxx/doc/binary-data.md index 7c85cf4b8..57d37e5a1 100644 --- a/include/pqxx/doc/binary-data.md +++ b/include/pqxx/doc/binary-data.md @@ -9,8 +9,8 @@ Generally you'll want to use `BYTEA` for reasonably-sized values, and large objects for very large values. That's the database side. On the C++ side, in libpqxx, all binary data must be -either `pqxx::bytes` or `pqxx::bytes_view`; or if you're building in C++20 or -better, anything that's a block of contiguous `std::byte` in memory. +either `pqxx::bytes` or `pqxx::bytes_view`, or anything else that's a block of +contiguous `std::byte` in memory. So for example, if you want to write a large object, you'd create a `pqxx::blob` object. And you might use that to write data in the form of diff --git a/include/pqxx/internal/conversions.hxx b/include/pqxx/internal/conversions.hxx index 844f81cbe..25f3678e4 100644 --- a/include/pqxx/internal/conversions.hxx +++ b/include/pqxx/internal/conversions.hxx @@ -103,9 +103,9 @@ inline char *generic_into_buf(char *begin, char *end, T const &value) } -// C++20: Guard with concept? +// TODO: Define type_traits directly? /// String traits for builtin integral types (though not bool). -template struct integral_traits +template struct integral_traits { static constexpr bool converts_to_string{true}; static constexpr bool converts_from_string{true}; diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index 5212cd6a8..9b684be0c 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -56,7 +56,6 @@ public: return m_params.size(); } - // C++20: noexcept. /// Get the number of parameters (signed). /** Unlike `size()`, this is not yet `noexcept`. That's because C++17's * `std::vector` does not have a `ssize()` member function. These member diff --git a/include/pqxx/prepared_statement.hxx b/include/pqxx/prepared_statement.hxx index e17b2b207..b3a89c149 100644 --- a/include/pqxx/prepared_statement.hxx +++ b/include/pqxx/prepared_statement.hxx @@ -39,8 +39,8 @@ namespace pqxx * zero. If you need a zero byte, you're dealing with binary strings, not * regular strings. Represent binary strings on the SQL side as `BYTEA` * (or as large objects). On the C++ side, use types like `pqxx::bytes` or - * `pqxx::bytes_view` or (in C++20) `std::vector`. Also, consider - * large objects on the SQL side and @ref blob on the C++ side. + * `pqxx::bytes_view` or `std::vector`. Also, consider large + * objects on the SQL side and @ref blob on the C++ side. * * @warning Passing the wrong number of parameters to a prepared or * parameterised statement will _break the connection._ The usual exception diff --git a/src/connection.cxx b/src/connection.cxx index 539b337d4..bddd7a0e0 100644 --- a/src/connection.cxx +++ b/src/connection.cxx @@ -639,9 +639,13 @@ int pqxx::connection::get_notifs() } auto const handler{m_notification_handlers.find(N->relname)}; - // C++20: Use "dot notation" to initialise struct fields. if (handler != std::end(m_notification_handlers)) - (handler->second)(notification{*this, channel, N->extra, N->be_pid}); + (handler->second)(notification{ + .conn=*this, + .channel=channel, + .payload=N->extra, + .backend_pid=N->be_pid, + }); N.reset(); } diff --git a/src/encodings.cxx b/src/encodings.cxx index f583f644b..fee1e585a 100644 --- a/src/encodings.cxx +++ b/src/encodings.cxx @@ -65,8 +65,7 @@ pqxx::internal::encoding_group enc_group(std::string_view encoding_name) return pqxx::internal::encoding_group::BIG5; [[unlikely]] break; case 'E': - // C++20: Use string_view::starts_with(). - if ((sz >= 6u) and (encoding_name.substr(0, 4) == "EUC_"sv)) + if (encoding_name.starts_with("EUC_"sv)) { auto const subtype{encoding_name.substr(4)}; static constexpr std::array subtypes{ @@ -90,8 +89,7 @@ pqxx::internal::encoding_group enc_group(std::string_view encoding_name) [[unlikely]] break; case 'I': // We know iso-8859-X, where 5 <= X < 9. They're all monobyte encodings. - // C++20: Use string_view::starts_with(). - if ((sz == 10) and (encoding_name.substr(0, 9) == "ISO_8859_"sv)) + if (encoding_name.starts_with("ISO_8859_"sv)) { char const subtype{encoding_name[9]}; if (('5' <= subtype) and (subtype < '9')) @@ -108,8 +106,7 @@ pqxx::internal::encoding_group enc_group(std::string_view encoding_name) [[unlikely]] break; case 'L': // We know LATIN1 through LATIN10. - // C++20: Use string_view::starts_with(). - if (encoding_name.substr(0, 5) == "LATIN"sv) + if (encoding_name.starts_with("LATIN"sv)) { auto const subtype{encoding_name.substr(5)}; if (subtype.size() == 1) @@ -140,7 +137,7 @@ pqxx::internal::encoding_group enc_group(std::string_view encoding_name) if (encoding_name == "UHC"sv) return pqxx::internal::encoding_group::UHC; else if (encoding_name == "UTF8"sv) - return pqxx::internal::encoding_group::UTF8; + [[likely]] return pqxx::internal::encoding_group::UTF8; [[unlikely]] break; case 'W': if (encoding_name.substr(0, 3) == "WIN"sv) diff --git a/src/sql_cursor.cxx b/src/sql_cursor.cxx index 2bc84b403..4a7c2537c 100644 --- a/src/sql_cursor.cxx +++ b/src/sql_cursor.cxx @@ -66,7 +66,6 @@ find_query_end(std::string_view query, pqxx::internal::encoding_group enc) if (enc == pqxx::internal::encoding_group::MONOBYTE) { // This is an encoding where we can scan backwards from the end. - // C++20: Use string_view::ends_with() and sub-view. while (end > 0 and useless_trail(query[end - 1])) --end; } else diff --git a/src/strconv.cxx b/src/strconv.cxx index 0a491f494..109dd8cbc 100644 --- a/src/strconv.cxx +++ b/src/strconv.cxx @@ -146,7 +146,7 @@ inline char *wrap_to_chars(char *begin, char *end, T const &value) namespace pqxx::internal { -template +template // NOLINTNEXTLINE(readability-non-const-parameter) zview integral_traits::to_buf(char *begin, char *end, T const &value) { @@ -189,7 +189,7 @@ template zview integral_traits::to_buf( char *, char *, unsigned long long const &); -template +template char *integral_traits::into_buf(char *begin, char *end, T const &value) { // This is exactly what to_chars is good at. Trust standard library @@ -541,7 +541,7 @@ template std::string to_string_float(T value) namespace pqxx::internal { -template T integral_traits::from_string(std::string_view text) +template T integral_traits::from_string(std::string_view text) { return from_string_arithmetic(text); } diff --git a/test/unit/test_range.cxx b/test/unit/test_range.cxx index 4df6f5520..0272a889b 100644 --- a/test/unit/test_range.cxx +++ b/test/unit/test_range.cxx @@ -543,11 +543,7 @@ void test_range_conversion() constexpr void test_range_is_constexpr() { -// Test compile-time operations. -// -// A few things in the standard library need to be constexpr for this to work, -// so we only test it in C++20. -#if __cplusplus >= 202002L + // Test compile-time operations. using range = pqxx::range; using ibound = pqxx::inclusive_bound; @@ -556,7 +552,6 @@ constexpr void test_range_is_constexpr() static_assert(oneone == oneone); static_assert(oneone != onethree); static_assert(onethree.contains(oneone)); -#endif } diff --git a/tools/generate_cxx_checks.py b/tools/generate_cxx_checks.py index 878743679..7ac888b7e 100755 --- a/tools/generate_cxx_checks.py +++ b/tools/generate_cxx_checks.py @@ -3,7 +3,7 @@ """Generate autoconf/CMake checks for C++ feature check macros. Produces feature checks for those features that we can detect based on just -the C++20 feature check macros. +the C++ feature check macros. Reads the test libpqxx feature macro names, as well as the C++ feature check macros that control them, from cxx_features.txt in the source tree. From a55748447db103501e64e960f06be3aa0bab10e7 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 2 Jan 2025 22:42:34 +0100 Subject: [PATCH 52/57] Apply some more standard concepts. --- include/pqxx/internal/conversions.hxx | 4 ++-- src/strconv.cxx | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/pqxx/internal/conversions.hxx b/include/pqxx/internal/conversions.hxx index 25f3678e4..7694d70d5 100644 --- a/include/pqxx/internal/conversions.hxx +++ b/include/pqxx/internal/conversions.hxx @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -124,9 +125,8 @@ template struct integral_traits }; -// C++20: Guard with concept? /// String traits for builtin floating-point types. -template struct float_traits +template struct float_traits { static constexpr bool converts_to_string{true}; static constexpr bool converts_from_string{true}; diff --git a/src/strconv.cxx b/src/strconv.cxx index 109dd8cbc..e08d0365a 100644 --- a/src/strconv.cxx +++ b/src/strconv.cxx @@ -437,7 +437,7 @@ inline T PQXX_COLD from_string_awful_float(std::string_view text) namespace pqxx::internal { /// Floating-point to_buf implemented in terms of to_string. -template +template zview float_traits::to_buf(char *begin, char *end, T const &value) { #if defined(PQXX_HAVE_CHARCONV_FLOAT) @@ -476,7 +476,7 @@ template zview float_traits::to_buf(char *, char *, long double const &); -template +template char *float_traits::into_buf(char *begin, char *end, T const &value) { #if defined(PQXX_HAVE_CHARCONV_FLOAT) @@ -559,7 +559,8 @@ template unsigned long long integral_traits::from_string(std::string_view); -template T float_traits::from_string(std::string_view text) +template +T float_traits::from_string(std::string_view text) { #if defined(PQXX_HAVE_CHARCONV_FLOAT) return from_string_arithmetic(text); From a150eb91330c6b0228f00977cdba38a1a78825ec Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Thu, 9 Jan 2025 01:05:44 +0100 Subject: [PATCH 53/57] Assume concepts (update from master). --- config/Makefile.in | 2 +- test/unit/test_result_iteration.cxx | 2 -- test/unit/test_row.cxx | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/config/Makefile.in b/config/Makefile.in index 51586bda2..9b1454986 100644 --- a/config/Makefile.in +++ b/config/Makefile.in @@ -124,7 +124,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/test/unit/test_result_iteration.cxx b/test/unit/test_result_iteration.cxx index 495ff1eff..ff444e28b 100644 --- a/test/unit/test_result_iteration.cxx +++ b/test/unit/test_result_iteration.cxx @@ -12,9 +12,7 @@ void test_result_iteration() pqxx::connection cx; pqxx::work tx{cx}; pqxx::result r{tx.exec("SELECT generate_series(1, 3)")}; -#if defined(PQXX_HAVE_CONCEPTS) static_assert(std::forward_iterator); -#endif PQXX_CHECK(std::end(r) != std::begin(r), "Broken begin/end."); PQXX_CHECK(std::rend(r) != std::rbegin(r), "Broken rbegin/rend."); diff --git a/test/unit/test_row.cxx b/test/unit/test_row.cxx index 378375efe..7beaa5f03 100644 --- a/test/unit/test_row.cxx +++ b/test/unit/test_row.cxx @@ -11,9 +11,7 @@ void test_row() pqxx::connection cx; pqxx::work tx{cx}; pqxx::row r{tx.exec("SELECT 1, 2, 3").one_row()}; -#if defined(PQXX_HAVE_CONCEPTS) static_assert(std::forward_iterator); -#endif PQXX_CHECK_EQUAL(std::size(r), 3, "Unexpected row size."); PQXX_CHECK_EQUAL(r.at(0).as(), 1, "Wrong value at index 0."); PQXX_CHECK(std::begin(r) != std::end(r), "Broken row iteration."); From 50c8d03f6a1afce5d5e72f1e1caf9555193947ab Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 13 Jan 2025 01:14:29 +0100 Subject: [PATCH 54/57] Format. --- src/connection.cxx | 8 ++++---- src/encodings.cxx | 4 ++-- src/strconv.cxx | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/connection.cxx b/src/connection.cxx index bddd7a0e0..18db0a470 100644 --- a/src/connection.cxx +++ b/src/connection.cxx @@ -641,10 +641,10 @@ int pqxx::connection::get_notifs() auto const handler{m_notification_handlers.find(N->relname)}; if (handler != std::end(m_notification_handlers)) (handler->second)(notification{ - .conn=*this, - .channel=channel, - .payload=N->extra, - .backend_pid=N->be_pid, + .conn = *this, + .channel = channel, + .payload = N->extra, + .backend_pid = N->be_pid, }); N.reset(); diff --git a/src/encodings.cxx b/src/encodings.cxx index fee1e585a..2e8d80b4c 100644 --- a/src/encodings.cxx +++ b/src/encodings.cxx @@ -136,8 +136,8 @@ pqxx::internal::encoding_group enc_group(std::string_view encoding_name) case 'U': if (encoding_name == "UHC"sv) return pqxx::internal::encoding_group::UHC; - else if (encoding_name == "UTF8"sv) - [[likely]] return pqxx::internal::encoding_group::UTF8; + else if (encoding_name == "UTF8"sv) [[likely]] + return pqxx::internal::encoding_group::UTF8; [[unlikely]] break; case 'W': if (encoding_name.substr(0, 3) == "WIN"sv) diff --git a/src/strconv.cxx b/src/strconv.cxx index e08d0365a..e19509311 100644 --- a/src/strconv.cxx +++ b/src/strconv.cxx @@ -541,7 +541,8 @@ template std::string to_string_float(T value) namespace pqxx::internal { -template T integral_traits::from_string(std::string_view text) +template +T integral_traits::from_string(std::string_view text) { return from_string_arithmetic(text); } From 5449a009127f301d3530c0748a3853f0e4ee2e24 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Sun, 19 Jan 2025 13:52:58 +0100 Subject: [PATCH 55/57] String conversion to `string_view`. More binary types. (#926) * Retire `binarystring`. This type has been deprecated since 7.2.0, more than 4 years ago. * String conversion to `string_view`. More binary types. Fixes: https://github.com/jtv/libpqxx/issues/694 Fixes: https://github.com/jtv/libpqxx/issues/827 Making much broader use of concepts. String conversions now accept any contiguous range of `std::byte` as binary data. Traits specialisations for integer and floating-point types are simpler now. And you can now just convert from string to `std::string_view` (or `char const *`), so long as you don't access it after the original string's lifetime ends. * Work around Visual Studio 2022 concepts problem. This compiler was having trouble with the syntax I used to specialise the generic `string_traits` template to a _concept_ `T` (as opposed to run-of-the-mill specialisation to a _type_ `T`). So just for the floating-point string traits, I went back to the old setup where I had a separate implementation type template (`string_float_traits`) and derived the `string_traits` implementations for those types from instantiations of that template. * Forbid string conversion from `char const *`. It was stupid of me to allow this. I hope nobody ever used it. --- NEWS | 4 + config/Makefile.in | 2 +- include/pqxx/doc/datatypes.md | 27 ++- include/pqxx/field.hxx | 45 +--- include/pqxx/internal/conversions.hxx | 321 ++++++++------------------ include/pqxx/params.hxx | 2 + include/pqxx/strconv.hxx | 18 +- include/pqxx/types.hxx | 42 +++- include/pqxx/util.hxx | 16 +- src/strconv.cxx | 214 ++++++++--------- test/unit/test_string_conversion.cxx | 18 ++ 11 files changed, 291 insertions(+), 418 deletions(-) diff --git a/NEWS b/NEWS index b5b9055b4..4c3e0a601 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,9 @@ 8.0.0 - C++20 is now the oldest C++ version that libpqxx supports. + - "String conversion" to `std::string_view` is now supported. (#694) + - **Beware lifetime** when "converting" a string to `std::string_view`! + - Conversion from string to `char const *` is no longer allowed. + - Binary data can be any `std::contiguous_range` of `std::byte`. (#925) - Retired `binarystring` and its headers. Use `blob` instead. - Retired `connection_base` type alias. Use `connection`. - Retired `pqxx::encrypt_password()`. Use the ones in `pqxx::connection`. diff --git a/config/Makefile.in b/config/Makefile.in index 9b1454986..51586bda2 100644 --- a/config/Makefile.in +++ b/config/Makefile.in @@ -124,7 +124,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 depcomp install-sh ltmain.sh missing mkinstalldirs + config.sub install-sh ltmain.sh missing mkinstalldirs DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ diff --git a/include/pqxx/doc/datatypes.md b/include/pqxx/doc/datatypes.md index 4584f24b8..b5bc463ee 100644 --- a/include/pqxx/doc/datatypes.md +++ b/include/pqxx/doc/datatypes.md @@ -12,8 +12,8 @@ You can "teach" libpqxx (in the scope of your own application) to convert additional types of values to and from PostgreSQL's string format. This is massively useful, but it's not for the faint of heart. You'll need to -specialise some templates. And, **the API for doing this can change with any -major libpqxx release.** +specialise several templates. And, **the API for doing this can change with +any major libpqxx release.** If that happens, your code may fail to compile with the newer libpqxx version, and you'll have to go through the `NEWS` file to find the API changes. Usually @@ -107,13 +107,16 @@ namespace near the top of your translation unit, and pass the type as an argument. The library also provides specialisations for `std::optional`, -`std::shared_ptr`, and `std::unique_ptr`. If you have conversions for -`T`, you'll also automatically have conversions for those. +`std::shared_ptr`, and `std::unique_ptr` (for any given `T`). If you +have conversions for `T`, you'll also automatically have conversions for those. Specialise `type_name` ---------------------- +(This is a feature that should disappear once we have introspection in the C++ +language.) + When errors happen during conversion, libpqxx will compose error messages for the user. Sometimes these will include the name of the type that's being converted. @@ -142,12 +145,16 @@ Specialise `nullness` --------------------- A struct template `pqxx::nullness` defines whether your type has a natural -"null value" built in. If so, it also provides member functions for producing -and recognising null values. +"null value" built in. For example, a `std::optional` instantiation has a +value that neatly maps to an SQL null: the un-initialised state. + +If your type has a value like that, its `pqxx::nullness` specialisation also +provides member functions for producing and recognising null values. The simplest scenario is also the most common: most types don't have a null value built in. There is no "null `int`" in C++. In that kind of case, just -derive your nullness traits from `pqxx::no_null` as a shorthand: +derive your nullness traits from `pqxx::no_null` as a shorthand: This tells +libpqxx that your type has no null value of its own. ```cxx // T is your type. @@ -196,9 +203,9 @@ where `NULL <> NULL`). Or `T` may have multiple different null values. Or `T` may override the comparison operator to behave in some unusual way. As a third case, your type may be one that _always_ represents a null value. -This is the case for `std::nullptr_t` and `std::nullopt_t`. In that case, you -set `nullness::always_null` to `true` (as well as `has_null` of course), -and you won't need to define any actual conversions. +This is the case for `std::nullptr_t` and `std::nullopt_t`. In a case like +that, you set `nullness::always_null` to `true` (as well as `has_null` +of course), and you won't need to define any actual conversions. Specialise `string_traits` diff --git a/include/pqxx/field.hxx b/include/pqxx/field.hxx index e1fe709c3..16fe5a93c 100644 --- a/include/pqxx/field.hxx +++ b/include/pqxx/field.hxx @@ -344,44 +344,13 @@ template<> inline bool field::to(char const *&obj) const } -template<> inline bool field::to(std::string_view &obj) const -{ - bool const null{is_null()}; - if (not null) - obj = view(); - return not null; -} - - -template<> -inline bool field::to( - std::string_view &obj, std::string_view const &default_value) const -{ - bool const null{is_null()}; - if (null) - obj = default_value; - else - obj = view(); - return not null; -} - - -template<> inline std::string_view field::as() const -{ - if (is_null()) - internal::throw_null_conversion(type_name); - return view(); -} - - -template<> -inline std::string_view -field::as(std::string_view const &default_value) const -{ - return is_null() ? default_value : view(); -} - - +/// Specialization: `to(zview &)`. +/** This conversion is not generally available, since the general conversion + * would not know whether there was indeed a terminating zero at the end of + * the string. (It could check, but it would have no way of knowing that a + * zero occurring after the string in memory was actually part of the same + * allocation.) + */ template<> inline bool field::to(zview &obj) const { bool const null{is_null()}; diff --git a/include/pqxx/internal/conversions.hxx b/include/pqxx/internal/conversions.hxx index 7694d70d5..f03c32ef8 100644 --- a/include/pqxx/internal/conversions.hxx +++ b/include/pqxx/internal/conversions.hxx @@ -70,7 +70,8 @@ throw_null_conversion(std::string_view type); * ensure that the compiler disallows their use. The compiler error message * will at least contain a hint of the root of the problem. */ -template struct disallowed_ambiguous_char_conversion +template +struct disallowed_ambiguous_char_conversion { static constexpr bool converts_to_string{false}; static constexpr bool converts_from_string{false}; @@ -104,34 +105,24 @@ inline char *generic_into_buf(char *begin, char *end, T const &value) } -// TODO: Define type_traits directly? -/// String traits for builtin integral types (though not bool). -template struct integral_traits +/// String traits for builtin floating-point types. +/** It _would_ make sense to define this directly as the definition for + * `pqxx::string_traits` where `T` is a `std::floating_point`. However + * Viual Studio 2022 does not seem to accept that syntax. + * + * So instead, we create a separate base class for `std::floating_point` types + * and then derive specialisatinos of `pqxx::string_traits` from that. + */ +template struct float_string_traits { static constexpr bool converts_to_string{true}; static constexpr bool converts_from_string{true}; - static PQXX_LIBEXPORT T from_string(std::string_view text); - static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value); - static PQXX_LIBEXPORT char *into_buf(char *begin, char *end, T const &value); - static constexpr std::size_t size_buffer(T const &) noexcept - { - /** Includes a sign if needed; the number of base-10 digits which the type - * can reliably represent; the one extra base-10 digit which the type can - * only partially represent; and the terminating zero. - */ - return std::is_signed_v + std::numeric_limits::digits10 + 1 + 1; - } -}; + static PQXX_LIBEXPORT T from_string(std::string_view text); + static PQXX_LIBEXPORT pqxx::zview + to_buf(char *begin, char *end, T const &value); -/// String traits for builtin floating-point types. -template struct float_traits -{ - static constexpr bool converts_to_string{true}; - static constexpr bool converts_from_string{true}; - static PQXX_LIBEXPORT T from_string(std::string_view text); - static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value); static PQXX_LIBEXPORT char *into_buf(char *begin, char *end, T const &value); // Return a nonnegative integral value's number of decimal digits. @@ -199,46 +190,44 @@ struct nullness>> : no_null {}; -template<> struct string_traits : internal::integral_traits -{}; -template<> inline constexpr bool is_unquoted_safe{true}; -template<> -struct string_traits - : internal::integral_traits -{}; -template<> inline constexpr bool is_unquoted_safe{true}; -template<> struct string_traits : internal::integral_traits -{}; -template<> inline constexpr bool is_unquoted_safe{true}; -template<> struct string_traits : internal::integral_traits -{}; -template<> inline constexpr bool is_unquoted_safe{true}; -template<> struct string_traits : internal::integral_traits -{}; -template<> inline constexpr bool is_unquoted_safe{true}; -template<> -struct string_traits : internal::integral_traits -{}; -template<> inline constexpr bool is_unquoted_safe{true}; +/// String traits for builtin integer types. +/** This does not cover `bool` or (unlike `std::integral`) the `char` types. + */ +template struct string_traits +{ + static constexpr bool converts_to_string{true}; + static constexpr bool converts_from_string{true}; + static PQXX_LIBEXPORT T from_string(std::string_view text); + static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value); + static PQXX_LIBEXPORT char *into_buf(char *begin, char *end, T const &value); + + static constexpr std::size_t size_buffer(T const &) noexcept + { + /** Includes a sign if needed; the number of base-10 digits which the type + * can reliably represent; the one extra base-10 digit which the type can + * only partially represent; and the terminating zero. + */ + return std::is_signed_v + std::numeric_limits::digits10 + 1 + 1; + } +}; + + +template +inline constexpr bool is_unquoted_safe{true}; +template +inline constexpr bool is_unquoted_safe{true}; + + template<> -struct string_traits : internal::integral_traits +struct string_traits : pqxx::internal::float_string_traits {}; -template<> inline constexpr bool is_unquoted_safe{true}; template<> -struct string_traits - : internal::integral_traits -{}; -template<> inline constexpr bool is_unquoted_safe{true}; -template<> struct string_traits : internal::float_traits +struct string_traits : pqxx::internal::float_string_traits {}; -template<> inline constexpr bool is_unquoted_safe{true}; -template<> struct string_traits : internal::float_traits -{}; -template<> inline constexpr bool is_unquoted_safe{true}; template<> -struct string_traits : internal::float_traits +struct string_traits + : pqxx::internal::float_string_traits {}; -template<> inline constexpr bool is_unquoted_safe{true}; template<> struct string_traits @@ -489,12 +478,21 @@ template<> struct nullness /// String traits for C-style string ("pointer to char const"). +/** This conversion is not bidirectional. You can convert a C-style string to + * an SQL string, but not the other way around. + * + * The reason for this is the terminating zero. The incoming SQL string is a + * `std::string_view`, which may or may not have a zero at the end. (And + * there's no reliable way of checking, since the next memory position may not + * be a valid address. Even if there happens to be a zero there, it isn't + * necessarily part of the same block of mmory.) + */ template<> struct string_traits { static constexpr bool converts_to_string{true}; - static constexpr bool converts_from_string{true}; + static constexpr bool converts_from_string{false}; - static char const *from_string(std::string_view text) { return text.data(); } + static char const *from_string(std::string_view text) =delete; static zview to_buf(char *begin, char *end, char const *const &value) { @@ -537,6 +535,15 @@ template<> struct nullness /// String traits for non-const C-style string ("pointer to char"). +/** This conversion is not bidirectional. You can convert a `char *` to an + * SQL string, but not vice versa. + * + * There are two reasons. One is the fact that an SQL string arrives in the + * form of a `std::string_view`; there is no guarantee of a trailing zero. + * + * The other reason is constness. We can't give you a non-const pointer into + * a string that was handed into the conversion as `const`. + */ template<> struct string_traits { static constexpr bool converts_to_string{true}; @@ -558,7 +565,6 @@ template<> struct string_traits return string_traits::size_buffer(value); } - /// Don't allow conversion to this type since it breaks const-safety. static char *from_string(std::string_view) = delete; }; @@ -590,6 +596,7 @@ template struct string_traits std::memcpy(begin, value, N); return begin + N; } + static constexpr std::size_t size_buffer(char const (&)[N]) noexcept { return N; @@ -646,10 +653,15 @@ template<> struct nullness : no_null /// String traits for `string_view`. +/** @warning This conversion does not store the string's contents anywhere. + * When you convert a string to a `std::string_view`, _do not access the + * resulting view after the original string has been destroyed. The contents + * will no longer be valid, even though tests may not make this obvious. + */ template<> struct string_traits { static constexpr bool converts_to_string{true}; - static constexpr bool converts_from_string{false}; + static constexpr bool converts_from_string{true}; static constexpr std::size_t size_buffer(std::string_view const &value) noexcept @@ -674,8 +686,7 @@ template<> struct string_traits return generic_to_buf(begin, end, value); } - /// Don't convert to this type; it has nowhere to store its contents. - static std::string_view from_string(std::string_view) = delete; + static std::string_view from_string(std::string_view value) { return value; } }; @@ -711,7 +722,11 @@ template<> struct string_traits return {begin, static_cast(stop - begin - 1)}; } - /// Don't convert to this type; it has nowhere to store its contents. + /// Don't convert to this type. There may not be a terminating zero. + /** There is no valid way to figure out here whether there is a terminating + * zero. Even if there is one, that may just be the first byte of an + * entirely separately allocated piece of memory. + */ static zview from_string(std::string_view) = delete; }; @@ -893,20 +908,10 @@ inline constexpr bool is_unquoted_safe>{ is_unquoted_safe}; -template<> struct nullness : no_null -{}; - - template struct nullness : no_null {}; -template inline constexpr format param_format(DATA const &) -{ - return format::binary; -} - - template struct string_traits { static constexpr bool converts_to_string{true}; @@ -937,90 +942,11 @@ template struct string_traits auto const size{pqxx::internal::size_unesc_bin(std::size(text))}; bytes buf; buf.resize(size); + // XXX: Use std::as_writable_bytes. pqxx::internal::unesc_bin(text, reinterpret_cast(buf.data())); return buf; } }; - - -template<> struct string_traits -{ - static constexpr bool converts_to_string{true}; - static constexpr bool converts_from_string{true}; - - static std::size_t size_buffer(bytes const &value) noexcept - { - return internal::size_esc_bin(std::size(value)); - } - - static zview to_buf(char *begin, char *end, bytes const &value) - { - return generic_to_buf(begin, end, value); - } - - static char *into_buf(char *begin, char *end, bytes const &value) - { - auto const budget{size_buffer(value)}; - if (std::cmp_less(end - begin, budget)) - throw conversion_overrun{ - "Not enough buffer space to escape binary data."}; - internal::esc_bin(value, begin); - return begin + budget; - } - - static bytes from_string(std::string_view text) - { - auto const size{pqxx::internal::size_unesc_bin(std::size(text))}; - bytes buf; - buf.resize(size); - pqxx::internal::unesc_bin(text, reinterpret_cast(buf.data())); - return buf; - } -}; - - -template<> inline constexpr format param_format(bytes const &) -{ - return format::binary; -} - - -template<> struct nullness : no_null -{}; - - -template<> struct string_traits -{ - static constexpr bool converts_to_string{true}; - static constexpr bool converts_from_string{false}; - - static std::size_t size_buffer(bytes_view const &value) noexcept - { - return internal::size_esc_bin(std::size(value)); - } - - static zview to_buf(char *begin, char *end, bytes_view const &value) - { - return generic_to_buf(begin, end, value); - } - - static char *into_buf(char *begin, char *end, bytes_view const &value) - { - auto const budget{size_buffer(value)}; - if (std::cmp_less(end - begin, budget)) - throw conversion_overrun{ - "Not enough buffer space to escape binary data."}; - internal::esc_bin(value, begin); - return begin + budget; - } - - // There's no from_string, because there's nobody to hold the data. -}; - -template<> inline constexpr format param_format(bytes_view const &) -{ - return format::binary; -} } // namespace pqxx @@ -1147,99 +1073,32 @@ public: namespace pqxx { -template -struct nullness> : no_null> -{}; - - -template -struct string_traits> - : internal::array_string_traits> -{}; - - -/// We don't know how to pass array params in binary format, so pass as text. -template -inline constexpr format param_format(std::vector const &) -{ - return format::text; -} - - -/// A `std::vector` is a binary string. Other vectors are not. -template -inline constexpr format param_format(std::vector const &) -{ - return format::binary; -} - - -template inline constexpr bool is_sql_array>{true}; - - -template -struct nullness> : no_null> -{}; - - -template -struct string_traits> - : internal::array_string_traits> -{}; - - -template -inline constexpr format param_format(std::span const &) -{ - return format::text; -} - - -template -inline constexpr format param_format(std::span const &) -{ - return format::binary; -} - - -template -inline constexpr bool is_sql_array>{true}; - - -template -struct nullness> : no_null> +template struct nullness : no_null {}; -template -struct string_traits> - : internal::array_string_traits> +template +struct string_traits : internal::array_string_traits {}; /// We don't know how to pass array params in binary format, so pass as text. -template -inline constexpr format param_format(std::array const &) +template inline constexpr format param_format(T const &) { return format::text; } -/// An array of `std::byte` is a binary string. -template -inline constexpr format param_format(std::array const &) +/// A contiguous range of `std::byte` is a binary string; other ranges are not. +template inline constexpr format param_format(T const &) { return format::binary; } -template -inline constexpr bool is_sql_array>{true}; -} // namespace pqxx +template inline constexpr bool is_sql_array{true}; -namespace pqxx -{ template inline std::string to_string(T const &value) { if (is_null(value)) @@ -1261,15 +1120,15 @@ template inline std::string to_string(T const &value) template<> inline std::string to_string(float const &value) { - return internal::to_string_float(value); + return pqxx::internal::to_string_float(value); } template<> inline std::string to_string(double const &value) { - return internal::to_string_float(value); + return pqxx::internal::to_string_float(value); } template<> inline std::string to_string(long double const &value) { - return internal::to_string_float(value); + return pqxx::internal::to_string_float(value); } template<> inline std::string to_string(std::stringstream const &value) { diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index 9b684be0c..f945ff4dd 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -106,8 +106,10 @@ public: /// Append a non-null binary parameter. void append(bytes &&) &; + /// Append all parameters in `value`. void append(params const &value) &; + /// Append all parameters in `value`. void append(params &&value) &; /// Append a non-null parameter, converting it to its string diff --git a/include/pqxx/strconv.hxx b/include/pqxx/strconv.hxx index 590b58c59..cab0387bc 100644 --- a/include/pqxx/strconv.hxx +++ b/include/pqxx/strconv.hxx @@ -198,6 +198,11 @@ template struct string_traits * for a value of this type. * * @warning A null value has no string representation. Do not parse a null. + * + * @warning If you convert a string to `std::string_view`, you're basically + * just getting a pointer into the original buffer. So, the `string_view` + * will become invalid when the original string's lifetime ends, or gets + * overwritten. Do not access the `string_view` you got after that! */ [[nodiscard]] static inline TYPE from_string(std::string_view text); @@ -590,19 +595,6 @@ inline zview generic_to_buf(char *begin, char *end, TYPE const &value) else return {begin, traits::into_buf(begin, end, value) - begin - 1}; } - - -/// Concept: Binary string, akin to @c std::string for binary data. -/** Any type that satisfies this concept can represent an SQL BYTEA value. - * - * A @c binary has a @c begin(), @c end(), @c size(), and @data(). Each byte - * is a @c std::byte, and they must all be laid out contiguously in memory so - * we can reference them by a pointer. - */ -template -concept binary = - std::ranges::contiguous_range and - std::is_same_v>, std::byte>; //@} } // namespace pqxx diff --git a/include/pqxx/types.hxx b/include/pqxx/types.hxx index 28185fb36..eba1909e3 100644 --- a/include/pqxx/types.hxx +++ b/include/pqxx/types.hxx @@ -102,6 +102,30 @@ concept potential_binary = std::ranges::contiguous_range and (sizeof(value_type) == 1); +/// Concept: Binary string, akin to @c std::string for binary data. +/** Any type that satisfies this concept can represent an SQL BYTEA value. + * + * A @c binary has a @c begin(), @c end(), @c size(), and @data(). Each byte + * is a @c std::byte, and they must all be laid out contiguously in memory so + * we can reference them by a pointer. + */ +template +concept binary = + std::ranges::contiguous_range and + std::same_as< + std::remove_cvref_t>, std::byte>; + + +/// A series of something that's not bytes. +template +concept nonbinary_range = + std::ranges::range and + not std::same_as< + std::remove_cvref_t>, std::byte> and + not std::same_as< + std::remove_cvref_t>, char>; + + /// Marker for @ref stream_from constructors: "stream from table." /** @deprecated Use @ref stream_from::table() instead. */ @@ -113,6 +137,22 @@ struct from_table_t */ struct from_query_t {}; - } // namespace pqxx + + +namespace pqxx::internal +{ +/// Concept: one of the "char" types. +template +concept char_type = std::same_as, char> or + std::same_as, signed char> or + std::same_as, unsigned char>; + + +/// Concept: an integral number type. +/** Unlike `std::integral`, this does not include the `char` types. + */ +template +concept integer = std::integral and not char_type; +} // namespace pqxx::internal #endif diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index 7cb17e62e..669b47104 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -334,7 +334,7 @@ using bytes_view = std::conditional< * @warning You must keep the storage holding the actual data alive for as * long as you might use this function's return value. */ -template bytes_view binary_cast(TYPE const &data) +template inline bytes_view binary_cast(TYPE const &data) { static_assert(sizeof(value_type) == 1); // C++20: Use std::as_bytes. @@ -453,7 +453,7 @@ inline constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept } -// TODO: Use actual binary type for "data". +// XXX: Maybe pass a span so we can check length? /// Hex-escape binary data into a buffer. /** The buffer must be able to accommodate * `size_esc_bin(std::size(binary_data))` bytes, and the function will write @@ -463,6 +463,18 @@ inline constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept void PQXX_LIBEXPORT esc_bin(bytes_view binary_data, char buffer[]) noexcept; +/// Hex-escape binary data into a buffer. +/** The buffer must be able to accommodate + * `size_esc_bin(std::size(binary_data))` bytes, and the function will write + * exactly that number of bytes into the buffer. This includes a trailing + * zero. + */ +template inline void esc_bin(T &&binary_data, char buffer[]) noexcept +{ + esc_bin(binary_cast(binary_data), buffer); +} + + /// Hex-escape binary data into a std::string. std::string PQXX_LIBEXPORT esc_bin(bytes_view binary_data); diff --git a/src/strconv.cxx b/src/strconv.cxx index e19509311..c3293b6bf 100644 --- a/src/strconv.cxx +++ b/src/strconv.cxx @@ -144,11 +144,13 @@ inline char *wrap_to_chars(char *begin, char *end, T const &value) } // namespace -namespace pqxx::internal +namespace pqxx { -template -// NOLINTNEXTLINE(readability-non-const-parameter) -zview integral_traits::to_buf(char *begin, char *end, T const &value) +template +inline + // NOLINTNEXTLINE(readability-non-const-parameter) + zview + string_traits::to_buf(char *begin, char *end, T const &value) { static_assert(std::is_integral_v); auto const space{end - begin}, @@ -174,23 +176,8 @@ zview integral_traits::to_buf(char *begin, char *end, T const &value) } -template zview integral_traits::to_buf(char *, char *, short const &); -template zview integral_traits::to_buf( - char *, char *, unsigned short const &); -template zview integral_traits::to_buf(char *, char *, int const &); -template zview -integral_traits::to_buf(char *, char *, unsigned const &); -template zview integral_traits::to_buf(char *, char *, long const &); -template zview -integral_traits::to_buf(char *, char *, unsigned long const &); -template zview -integral_traits::to_buf(char *, char *, long long const &); -template zview integral_traits::to_buf( - char *, char *, unsigned long long const &); - - -template -char *integral_traits::into_buf(char *begin, char *end, T const &value) +template +inline char *string_traits::into_buf(char *begin, char *end, T const &value) { // This is exactly what to_chars is good at. Trust standard library // implementers to optimise better than we can. @@ -198,20 +185,15 @@ char *integral_traits::into_buf(char *begin, char *end, T const &value) } -template char *integral_traits::into_buf(char *, char *, short const &); -template char *integral_traits::into_buf( - char *, char *, unsigned short const &); -template char *integral_traits::into_buf(char *, char *, int const &); -template char * -integral_traits::into_buf(char *, char *, unsigned const &); -template char *integral_traits::into_buf(char *, char *, long const &); -template char *integral_traits::into_buf( - char *, char *, unsigned long const &); -template char * -integral_traits::into_buf(char *, char *, long long const &); -template char *integral_traits::into_buf( - char *, char *, unsigned long long const &); -} // namespace pqxx::internal +template struct string_traits; +template struct string_traits; +template struct string_traits; +template struct string_traits; +template struct string_traits; +template struct string_traits; +template struct string_traits; +template struct string_traits; +} // namespace pqxx namespace pqxx::internal @@ -321,12 +303,9 @@ template inline TYPE from_string_arithmetic(std::string_view in) else throw pqxx::conversion_error{base + ": " + msg}; } -} // namespace #if !defined(PQXX_HAVE_CHARCONV_FLOAT) -namespace -{ constexpr bool valid_infinity_string(std::string_view text) noexcept { return text == "inf" or text == "infinity" or text == "INFINITY" or @@ -430,69 +409,9 @@ inline T PQXX_COLD from_string_awful_float(std::string_view text) return result; } -} // namespace #endif // !PQXX_HAVE_CHARCONV_FLOAT -namespace pqxx::internal -{ -/// Floating-point to_buf implemented in terms of to_string. -template -zview float_traits::to_buf(char *begin, char *end, T const &value) -{ -#if defined(PQXX_HAVE_CHARCONV_FLOAT) - { - // Definitely prefer to let the standard library handle this! - auto const ptr{wrap_to_chars(begin, end, value)}; - return zview{begin, std::size_t(ptr - begin - 1)}; - } -#else - { - // Implement it ourselves. Weird detail: since this workaround is based on - // std::stringstream, which produces a std::string, it's actually easier to - // build the to_buf() on top of the to_string() than the other way around. - if (std::isnan(value)) - return "nan"_zv; - if (std::isinf(value)) - return (value > 0) ? "infinity"_zv : "-infinity"_zv; - auto text{to_string_float(value)}; - auto have{end - begin}; - auto need{std::size(text) + 1}; - if (need > std::size_t(have)) - throw conversion_error{ - "Could not convert floating-point number to string: " - "buffer too small. " + - state_buffer_overrun(have, need)}; - text.copy(begin, need); - return zview{begin, std::size(text)}; - } -#endif -} - - -template zview float_traits::to_buf(char *, char *, float const &); -template zview float_traits::to_buf(char *, char *, double const &); -template zview -float_traits::to_buf(char *, char *, long double const &); - - -template -char *float_traits::into_buf(char *begin, char *end, T const &value) -{ -#if defined(PQXX_HAVE_CHARCONV_FLOAT) - return wrap_to_chars(begin, end, value); -#else - return generic_into_buf(begin, end, value); -#endif -} - - -template char *float_traits::into_buf(char *, char *, float const &); -template char *float_traits::into_buf(char *, char *, double const &); -template char * -float_traits::into_buf(char *, char *, long double const &); - - #if !defined(PQXX_HAVE_CHARCONV_FLOAT) template inline std::string PQXX_COLD @@ -503,18 +422,21 @@ to_dumb_stringstream(dumb_stringstream &s, F value) return s.str(); } #endif +} // namespace +namespace pqxx::internal +{ /// Floating-point implementations for @c pqxx::to_string(). template std::string to_string_float(T value) { #if defined(PQXX_HAVE_CHARCONV_FLOAT) { - static constexpr auto space{float_traits::size_buffer(value)}; + static constexpr auto space{string_traits::size_buffer(value)}; std::string buf; buf.resize(space); std::string_view const view{ - float_traits::to_buf(std::data(buf), std::data(buf) + space, value)}; + string_traits::to_buf(std::data(buf), std::data(buf) + space, value)}; buf.resize(static_cast(std::end(view) - std::begin(view))); return buf; } @@ -536,46 +458,94 @@ template std::string to_string_float(T value) } #endif } -} // namespace pqxx::internal -namespace pqxx::internal -{ -template -T integral_traits::from_string(std::string_view text) +template +T float_string_traits::from_string(std::string_view text) { +#if defined(PQXX_HAVE_CHARCONV_FLOAT) return from_string_arithmetic(text); +#else + return from_string_awful_float(text); +#endif } -template short integral_traits::from_string(std::string_view); -template unsigned short - integral_traits::from_string(std::string_view); -template int integral_traits::from_string(std::string_view); -template unsigned integral_traits::from_string(std::string_view); -template long integral_traits::from_string(std::string_view); -template unsigned long - integral_traits::from_string(std::string_view); -template long long integral_traits::from_string(std::string_view); -template unsigned long long - integral_traits::from_string(std::string_view); + +template +zview float_string_traits::to_buf(char *begin, char *end, T const &value) +{ +#if defined(PQXX_HAVE_CHARCONV_FLOAT) + { + // Definitely prefer to let the standard library handle this! + auto const ptr{wrap_to_chars(begin, end, value)}; + return zview{begin, std::size_t(ptr - begin - 1)}; + } +#else + { + // Implement it ourselves. Weird detail: since this workaround is based + // on std::stringstream, which produces a std::string, it's actually + // easier to build the to_buf() on top of the to_string() than the other + // way around. + if (std::isnan(value)) + return "nan"_zv; + if (std::isinf(value)) + return (value > 0) ? "infinity"_zv : "-infinity"_zv; + auto text{to_string_float(value)}; + auto have{end - begin}; + auto need{std::size(text) + 1}; + if (need > std::size_t(have)) + throw conversion_error{ + "Could not convert floating-point number to string: " + "buffer too small. " + + state_buffer_overrun(have, need)}; + text.copy(begin, need); + return zview{begin, std::size(text)}; + } +#endif +} template -T float_traits::from_string(std::string_view text) +char *float_string_traits::into_buf(char *begin, char *end, T const &value) { #if defined(PQXX_HAVE_CHARCONV_FLOAT) - return from_string_arithmetic(text); + return wrap_to_chars(begin, end, value); #else - return from_string_awful_float(text); + return generic_into_buf(begin, end, value); #endif } -template float float_traits::from_string(std::string_view); -template double float_traits::from_string(std::string_view); -template long double float_traits::from_string(std::string_view); +template struct float_string_traits; +template struct float_string_traits; +template struct float_string_traits; +} // namespace pqxx::internal + + +namespace pqxx +{ +template +T string_traits::from_string(std::string_view text) +{ + return from_string_arithmetic(text); +} + +template short string_traits::from_string(std::string_view); +template unsigned short + string_traits::from_string(std::string_view); +template int string_traits::from_string(std::string_view); +template unsigned string_traits::from_string(std::string_view); +template long string_traits::from_string(std::string_view); +template unsigned long + string_traits::from_string(std::string_view); +template long long string_traits::from_string(std::string_view); +template unsigned long long + string_traits::from_string(std::string_view); +} // namespace pqxx +namespace pqxx::internal +{ template std::string to_string_float(float); template std::string to_string_float(double); template std::string to_string_float(long double); diff --git a/test/unit/test_string_conversion.cxx b/test/unit/test_string_conversion.cxx index 55486c666..44e4761c8 100644 --- a/test/unit/test_string_conversion.cxx +++ b/test/unit/test_string_conversion.cxx @@ -206,9 +206,27 @@ void test_string_view_conversion() } +void test_binary_converts_to_string() +{ + PQXX_CHECK_EQUAL( + pqxx::to_string(std::array{ + std::byte{0x41}, std::byte{0x42}, std::byte{0x43}}), + "\\x414243", "Bad conversino from std::array to string."); + + std::array x{std::byte{0x78}}; + PQXX_CHECK_EQUAL(std::size(x), 1u, "This vector is not what I thought."); + std::span span{x}; + PQXX_CHECK_EQUAL(std::size(span), 1u, "Strangely different span."); + PQXX_CHECK_EQUAL( + pqxx::to_string(span), "\\x78", + "Bad conversion from std::span to string."); +} + + PQXX_REGISTER_TEST(test_string_conversion); PQXX_REGISTER_TEST(test_convert_variant_to_string); PQXX_REGISTER_TEST(test_integer_conversion); PQXX_REGISTER_TEST(test_convert_null); PQXX_REGISTER_TEST(test_string_view_conversion); +PQXX_REGISTER_TEST(test_binary_converts_to_string); } // namespace From 34a5faa2bbf8547260ad0a1f2d44008aef9b07c0 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Sun, 19 Jan 2025 15:33:59 +0100 Subject: [PATCH 56/57] Make `is_null()` check `always_null` itself. Not sure how useful this is, but it's definitely more intuitive. Theoretically we might be able to leave `nullness::is_null()` on such types unimplemented altogether, though that might make things a little too irregular. --- NEWS | 1 + config/Makefile.in | 2 +- include/pqxx/connection.hxx | 5 ++--- include/pqxx/internal/conversions.hxx | 29 ++++++++++++++++++--------- include/pqxx/strconv.hxx | 5 ++++- test/unit/test_errorhandler.cxx | 4 ++-- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/NEWS b/NEWS index 4c3e0a601..ce6e780f5 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ - **Beware lifetime** when "converting" a string to `std::string_view`! - Conversion from string to `char const *` is no longer allowed. - Binary data can be any `std::contiguous_range` of `std::byte`. (#925) + - Generic `quote()` takes `always_null` into account. - Retired `binarystring` and its headers. Use `blob` instead. - Retired `connection_base` type alias. Use `connection`. - Retired `pqxx::encrypt_password()`. Use the ones in `pqxx::connection`. diff --git a/config/Makefile.in b/config/Makefile.in index 51586bda2..9b1454986 100644 --- a/config/Makefile.in +++ b/config/Makefile.in @@ -124,7 +124,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/connection.hxx b/include/pqxx/connection.hxx index 27a77a673..ff5805cb0 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -1383,14 +1383,13 @@ private: template inline std::string connection::quote(T const &t) const { - if constexpr (nullness::always_null) + // TODO: Can we leave the quotes out if unquoted_safe? + if (is_null(t)) { return "NULL"; } else { - if (is_null(t)) - return "NULL"; auto const text{to_string(t)}; // Okay, there's an easy way to do this and there's a hard way. The easy diff --git a/include/pqxx/internal/conversions.hxx b/include/pqxx/internal/conversions.hxx index f03c32ef8..3e80f9d95 100644 --- a/include/pqxx/internal/conversions.hxx +++ b/include/pqxx/internal/conversions.hxx @@ -1106,15 +1106,26 @@ template inline std::string to_string(T const &value) "Attempt to convert null " + std::string{type_name} + " to a string."}; - std::string buf; - // We can't just reserve() space; modifying the terminating zero leads to - // undefined behaviour. - buf.resize(size_buffer(value)); - auto const data{buf.data()}; - auto const end{ - string_traits::into_buf(data, data + std::size(buf), value)}; - buf.resize(static_cast(end - data - 1)); - return buf; + if constexpr (nullness>::always_null) + { + // Have to separate out this case: some functions in the "regular" code + // may not exist in the "always null" case. + PQXX_UNREACHABLE; + // C++23: The return may not be needed when std::unreachable is available. + return {}; + } + else + { + std::string buf; + // We can't just reserve() space; modifying the terminating zero leads to + // undefined behaviour. + buf.resize(size_buffer(value)); + auto const data{buf.data()}; + auto const end{ + string_traits::into_buf(data, data + std::size(buf), value)}; + buf.resize(static_cast(end - data - 1)); + return buf; + } } diff --git a/include/pqxx/strconv.hxx b/include/pqxx/strconv.hxx index cab0387bc..e0226bfb7 100644 --- a/include/pqxx/strconv.hxx +++ b/include/pqxx/strconv.hxx @@ -515,7 +515,10 @@ inline void into_string(TYPE const &value, std::string &out); template [[nodiscard]] inline constexpr bool is_null(TYPE const &value) noexcept { - return nullness>::is_null(value); + using base_type = std::remove_cvref_t; + using null_traits = nullness; + if constexpr (null_traits::always_null) return true; + else return null_traits::is_null(value); } diff --git a/test/unit/test_errorhandler.cxx b/test/unit/test_errorhandler.cxx index b6cd5d70a..edfa4f19f 100644 --- a/test/unit/test_errorhandler.cxx +++ b/test/unit/test_errorhandler.cxx @@ -42,8 +42,8 @@ template<> struct nullness // clang warns about these being unused. And clang 6 won't accept a // [[maybe_unused]] attribute on them either! - // static inline constexpr bool has_null{true}; - // static inline constexpr bool always_null{false}; + [[maybe_unused]] static inline constexpr bool has_null{true}; + static inline constexpr bool always_null{false}; static constexpr bool is_null(TestErrorHandler *e) noexcept { From afe64a3635d3332df3db4c2636c65365ef188032 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Mon, 20 Jan 2025 02:29:56 +0100 Subject: [PATCH 57/57] Accept `pqxx::binary` concept in more places. Fixes: https://github.com/jtv/libpqxx/issues/925 Extends a bunch more functions that accept `pqxx::bytes_view` with variants which accept any type that satisfies the `pqxx::binary` concept. Also, change the `pqxx::bytes_view` type alies from being a `std::basic_string_view` (which doesn't actually have to work according to the standard!) to being a `std::span`. This seems to be broadly compatible with existing code. For completeness I'm adding a `pqxx::writable_bytes_view` as well. Along the way I'm assuming support for C++17's `std::filesystem::path`, and adding a conversion to `pqxx::zview`. With that, `pqxx::blob` no longer needs explicit support for `std::filesystem::path` filenames; it just accepts `pqxx::zview` and passing a `std::filesystem::path` will just work. It avoids some ambiguities. --- NEWS | 3 +- cmake/pqxx_cxx_feature_checks.cmake | 4 -- config-tests/PQXX_HAVE_PATH.cxx | 9 ---- config/Makefile.in | 2 +- configitems | 1 - configure | 36 --------------- include/pqxx/blob.hxx | 71 ++++++++++------------------- include/pqxx/config.h.in | 3 -- include/pqxx/connection.hxx | 8 ++++ include/pqxx/doc/binary-data.md | 46 ++++++------------- include/pqxx/params.hxx | 2 + include/pqxx/types.hxx | 28 +++++++++--- include/pqxx/util.hxx | 48 +++++-------------- include/pqxx/zview.hxx | 9 ++++ pqxx_cxx_feature_checks.ac | 10 ---- src/blob.cxx | 24 +++++----- test/unit/test_blob.cxx | 10 ++-- 17 files changed, 110 insertions(+), 204 deletions(-) delete mode 100644 config-tests/PQXX_HAVE_PATH.cxx diff --git a/NEWS b/NEWS index ce6e780f5..aeb56dce7 100644 --- a/NEWS +++ b/NEWS @@ -17,7 +17,8 @@ - Assume compiler supports concepts. - Assume compiler supports integral conversions in `charconv`. - Assume compiler supports spans, ranges, and `cmp_less` etc. - - Assume compiler supports `std::remove_cvref_t`. + - Assume compiler supports `std::remove_cvref_t` etc. + - Assume compiler supports `std::filesystem::path`. - Assume compiler supports `[[likely]]` & `[[unlikely]]`. - Assume compiler supports `ssize()`. - Assume compiler supports ISO-646 without needing `` header. diff --git a/cmake/pqxx_cxx_feature_checks.cmake b/cmake/pqxx_cxx_feature_checks.cmake index b6fbe7e36..bb2f6253a 100644 --- a/cmake/pqxx_cxx_feature_checks.cmake +++ b/cmake/pqxx_cxx_feature_checks.cmake @@ -23,10 +23,6 @@ try_compile( PQXX_HAVE_MULTIDIM ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_MULTIDIM.cxx ) -try_compile( - PQXX_HAVE_PATH ${PROJECT_BINARY_DIR} - SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_PATH.cxx -) try_compile( PQXX_HAVE_POLL ${PROJECT_BINARY_DIR} SOURCES ${PROJECT_SOURCE_DIR}/config-tests/PQXX_HAVE_POLL.cxx diff --git a/config-tests/PQXX_HAVE_PATH.cxx b/config-tests/PQXX_HAVE_PATH.cxx deleted file mode 100644 index d93d37f5d..000000000 --- a/config-tests/PQXX_HAVE_PATH.cxx +++ /dev/null @@ -1,9 +0,0 @@ -// Check for working std::filesystem support. -#include - - -int main() -{ - // Apparently some versions of MinGW lack this comparison operator. - return std::filesystem::path{} != std::filesystem::path{}; -} diff --git a/config/Makefile.in b/config/Makefile.in index 9b1454986..51586bda2 100644 --- a/config/Makefile.in +++ b/config/Makefile.in @@ -124,7 +124,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 depcomp install-sh ltmain.sh missing mkinstalldirs + config.sub install-sh ltmain.sh missing mkinstalldirs DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ diff --git a/configitems b/configitems index 8208cd679..58af65a35 100644 --- a/configitems +++ b/configitems @@ -10,7 +10,6 @@ PQXX_HAVE_CXA_DEMANGLE internal compiler PQXX_HAVE_GCC_PURE public compiler PQXX_HAVE_GCC_VISIBILITY public compiler PQXX_HAVE_MULTIDIM public compiler -PQXX_HAVE_PATH public compiler PQXX_HAVE_POLL internal compiler PQXX_HAVE_SLEEP_FOR internal compiler PQXX_HAVE_SOURCE_LOCATION public compiler diff --git a/configure b/configure index 2e4d1dd41..21ef77e63 100755 --- a/configure +++ b/configure @@ -17500,42 +17500,6 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_MULTIDIM" >&5 printf "%s\n" "$PQXX_HAVE_MULTIDIM" >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_PATH" >&5 -printf %s "checking PQXX_HAVE_PATH... " >&6; } -PQXX_HAVE_PATH=yes -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -// Check for working std::filesystem support. - -#include - - - - - -int main() - -{ - - // Apparently some versions of MinGW lack this comparison operator. - - return std::filesystem::path{} != std::filesystem::path{}; - -} - - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO" -then : - -printf "%s\n" "#define PQXX_HAVE_PATH 1" >>confdefs.h - -else $as_nop - PQXX_HAVE_PATH=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PQXX_HAVE_PATH" >&5 -printf "%s\n" "$PQXX_HAVE_PATH" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PQXX_HAVE_POLL" >&5 printf %s "checking PQXX_HAVE_POLL... " >&6; } PQXX_HAVE_POLL=yes diff --git a/include/pqxx/blob.hxx b/include/pqxx/blob.hxx index 9621fd44e..dcd504934 100644 --- a/include/pqxx/blob.hxx +++ b/include/pqxx/blob.hxx @@ -19,10 +19,6 @@ #include -#if defined(PQXX_HAVE_PATH) -# include -#endif - #include #include @@ -88,6 +84,7 @@ public: */ static constexpr std::size_t chunk_limit = 0x7fffffff; + // XXX: Can we build a generic version of this? /// Read up to `size` bytes of the object into `buf`. /** Uses a buffer that you provide, resizing it as needed. If it suits you, * this lets you allocate the buffer once and then re-use it multiple times. @@ -106,7 +103,7 @@ public: * Returns the filled portion of `buf`. This may be empty. */ template - std::span read(std::span buf) + writable_bytes_view read(std::span buf) { return buf.subspan(0, raw_read(std::data(buf), std::size(buf))); } @@ -117,7 +114,7 @@ public: * * Returns the filled portion of `buf`. This may be empty. */ - template std::span read(DATA &buf) + template writable_bytes_view read(DATA &buf) { return {std::data(buf), raw_read(std::data(buf), std::size(buf))}; } @@ -143,7 +140,7 @@ public: */ template void write(DATA const &data) { - raw_write(std::data(data), std::size(data)); + return raw_write(binary_cast(data)); } /// Resize large object to `size` bytes. @@ -180,53 +177,47 @@ public: */ static oid from_buf(dbtransaction &tx, bytes_view data, oid id = 0); + /// Create a binary large object containing given `data`. + /** You may optionally specify an oid for the new object. If you do, and an + * object with that oid already exists, creation will fail. + */ + template + static oid from_buf(dbtransaction &tx, DATA data, oid id = 0) + { + return from_buf(tx, binary_cast(data), id); + } + /// Append `data` to binary large object. /** The underlying protocol only supports appending blocks up to 2 GB. */ static void append_from_buf(dbtransaction &tx, bytes_view data, oid id); - /// Read client-side file and store it server-side as a binary large object. - [[nodiscard]] static oid from_file(dbtransaction &, char const path[]); - -#if defined(PQXX_HAVE_PATH) && !defined(_WIN32) - /// Read client-side file and store it server-side as a binary large object. - /** This overload is not available on Windows, where `std::filesystem::path` - * converts to a `wchar_t` string rather than a `char` string. + /// Append `data` to binary large object. + /** The underlying protocol only supports appending blocks up to 2 GB. */ - [[nodiscard]] static oid - from_file(dbtransaction &tx, std::filesystem::path const &path) + template + static void append_from_buf(dbtransaction &tx, DATA data, oid id) { - return from_file(tx, path.c_str()); + append_from_buf(tx, binary_cast(data), id); } -#endif /// Read client-side file and store it server-side as a binary large object. - /** In this version, you specify the binary large object's oid. If that oid - * is already in use, the operation will fail. - */ - static oid from_file(dbtransaction &, char const path[], oid); + [[nodiscard]] static oid from_file(dbtransaction &, zview path); -#if defined(PQXX_HAVE_PATH) && !defined(_WIN32) /// Read client-side file and store it server-side as a binary large object. /** In this version, you specify the binary large object's oid. If that oid * is already in use, the operation will fail. - * - * This overload is not available on Windows, where `std::filesystem::path` - * converts to a `wchar_t` string rather than a `char` string. */ - static oid - from_file(dbtransaction &tx, std::filesystem::path const &path, oid id) - { - return from_file(tx, path.c_str(), id); - } -#endif + static oid from_file(dbtransaction &, zview path, oid); + // XXX: Can we build a generic version of this? /// Convenience function: Read up to `max_size` bytes from blob with `id`. /** You could easily do this yourself using the @ref open_r and @ref read * functions, but it can save you a bit of code to do it this way. */ static void to_buf(dbtransaction &, oid, bytes &, std::size_t max_size); + // XXX: Can we build a generic version of this? /// Read part of the binary large object with `id`, and append it to `buf`. /** Use this to break up a large read from one binary large object into one * massive buffer. Just keep calling this function until it returns zero. @@ -239,19 +230,7 @@ public: std::size_t append_max); /// Write a binary large object's contents to a client-side file. - static void to_file(dbtransaction &, oid, char const path[]); - -#if defined(PQXX_HAVE_PATH) && !defined(_WIN32) - /// Write a binary large object's contents to a client-side file. - /** This overload is not available on Windows, where `std::filesystem::path` - * converts to a `wchar_t` string rather than a `char` string. - */ - static void - to_file(dbtransaction &tx, oid id, std::filesystem::path const &path) - { - to_file(tx, id, path.c_str()); - } -#endif + static void to_file(dbtransaction &, oid, zview path); /// Close this blob. /** This does not delete the blob from the database; it only terminates your @@ -282,7 +261,7 @@ private: PQXX_PRIVATE std::string errmsg() const { return errmsg(m_conn); } PQXX_PRIVATE std::int64_t seek(std::int64_t offset, int whence); std::size_t raw_read(std::byte buf[], std::size_t size); - void raw_write(std::byte const buf[], std::size_t size); + void raw_write(bytes_view); connection *m_conn = nullptr; int m_fd = -1; diff --git a/include/pqxx/config.h.in b/include/pqxx/config.h.in index b79862a6b..0e9834a5b 100644 --- a/include/pqxx/config.h.in +++ b/include/pqxx/config.h.in @@ -75,9 +75,6 @@ /* Define if this feature is available. */ #undef PQXX_HAVE_MULTIDIM -/* Define if this feature is available. */ -#undef PQXX_HAVE_PATH - /* Define if this feature is available. */ #undef PQXX_HAVE_POLL diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index ff5805cb0..a24f63015 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -996,6 +996,14 @@ public: /// Escape and quote binary data for use as a BYTEA value in SQL statement. [[nodiscard]] std::string quote(bytes_view bytes) const; + // TODO: Make "into buffer" variant to eliminate a string allocation. + /// Escape and quote binary data for use as a BYTEA value in SQL statement. + template + [[nodiscard]] std::string quote(DATA data) const + { + return esc_raw(binary_cast(data)); + } + // TODO: Make "into buffer" variant to eliminate a string allocation. /// Escape string for literal LIKE match. /** Use this when part of an SQL "LIKE" pattern should match only as a diff --git a/include/pqxx/doc/binary-data.md b/include/pqxx/doc/binary-data.md index 57d37e5a1..e6d911dc4 100644 --- a/include/pqxx/doc/binary-data.md +++ b/include/pqxx/doc/binary-data.md @@ -9,51 +9,33 @@ Generally you'll want to use `BYTEA` for reasonably-sized values, and large objects for very large values. That's the database side. On the C++ side, in libpqxx, all binary data must be -either `pqxx::bytes` or `pqxx::bytes_view`, or anything else that's a block of -contiguous `std::byte` in memory. +some block of contiguous `std::byte` values in memory. That could be a +`std::vector`, or `std::span`, and so on. However the +_preferred_ types for binary data in libpqxx are... +* `pqxx::bytes` for storing the data, similar to `std::string` for text. +* `pqxx::bytes_view` for reading data stored elsewhere, similar to how you'd + use `std::string_view` for text. +* `pqxx::writable_bytes_view` for writing to data stored elsewhere. So for example, if you want to write a large object, you'd create a -`pqxx::blob` object. And you might use that to write data in the form of -`pqxx::bytes_view`. - -Your particular binary data may look different though. You may have it in a -`std::string`, or a `std::vector`, or a pointer to `char` -accompanied by a size (which could be signed or unsigned, and of any of a few -different widths). Sometimes that's your choice, or sometimes some other -library will dictate what form it takes. +`pqxx::blob` object. You might use that to write data which you pass in the +form of a `pqxx::bytes_view`. You might then read that data back by letting +`pqxx::blob` write the data into a `pqxx::bytes &` or a +`pqxx::writable_bytes_view` that you give it. So long as it's _basically_ still a block of bytes though, you can use -`pqxx::binary_cast` to construct a `pqxx::bytes_view` from it. - -There are two forms of `binary_cast`. One takes a single argument that must -support `std::data()` and `std::size()`: +`pqxx::binary_cast` to construct a `pqxx::bytes_view` from it: ```cxx std::string hi{"Hello binary world"}; my_blob.write(pqxx::binary_cast(hi); ``` -The other takes a pointer and a size: +For convenience there's also a form of `binary_cast` that takes a pointer and +a length. ```cxx char const greeting[] = "Hello binary world"; char const *hi = greeting; my_blob.write(pqxx::binary_cast(hi, sizeof(greeting))); ``` - - -Caveats -------- - -There are some restrictions on `binary_cast` that you must be aware of. - -First, your data must of a type that gives us _bytes._ So: `char`, -`unsigned char`, `signed char`, `int8_t`, `uint8_t`, or of course `std::byte`. -You can't feed in a vector of `double`, or anything like that. - -Second, the data must be laid out as a contiguous block in memory. If there's -no `std::data()` implementation for your type, it's not suitable. - -Third, `binary_cast` only constructs something like a `std::string_view`. It -does not make a copy of your actual data. So, make sure that your data remains -alive and in the same place while you're using it. diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index f945ff4dd..52d3f8340 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -82,6 +82,8 @@ public: /// Append a non-null string parameter. void append(std::string &&) &; + // XXX: Rethink the view/copy situation. + // XXX: Generic pqxx::binary support. /// Append a non-null binary parameter. /** The underlying data must stay valid for as long as the `params` * remains active. diff --git a/include/pqxx/types.hxx b/include/pqxx/types.hxx index eba1909e3..8c0fc98c3 100644 --- a/include/pqxx/types.hxx +++ b/include/pqxx/types.hxx @@ -85,21 +85,30 @@ using value_type = std::remove_cvref_t()))>; +/// A type one byte in size. +template +concept char_sized = (sizeof(CHAR) == 1); + + /// Concept: Any type that we can read as a string of `char`. template concept char_string = std::ranges::contiguous_range and - std::same_as>, char>; + char_sized> and + std::same_as>, value_type>; /// Concept: Anything we can iterate to get things we can read as strings. template -concept char_strings = std::ranges::range and - char_string>>; +concept char_strings = + std::ranges::range and + char_string>>; /// Concept: Anything we might want to treat as binary data. template concept potential_binary = - std::ranges::contiguous_range and (sizeof(value_type) == 1); + std::ranges::contiguous_range and + (sizeof(value_type) == 1) and + not std::is_reference_v>; /// Concept: Binary string, akin to @c std::string for binary data. @@ -112,8 +121,7 @@ concept potential_binary = template concept binary = std::ranges::contiguous_range and - std::same_as< - std::remove_cvref_t>, std::byte>; + std::same_as>, std::byte>; /// A series of something that's not bytes. @@ -126,6 +134,14 @@ concept nonbinary_range = std::remove_cvref_t>, char>; +/// Type alias for a view of bytes. +using bytes_view = std::span; + + +/// Type alias for a view of writable bytes. +using writable_bytes_view = std::span; + + /// Marker for @ref stream_from constructors: "stream from table." /** @deprecated Use @ref stream_from::table() instead. */ diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index 669b47104..643cd157b 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -296,7 +296,7 @@ inline constexpr bool has_generic_bytes_char_traits = // Necessary for libc++ 18. #include "pqxx/internal/ignore-deprecated-pre.hxx" -// C++20: Change this type. +// XXX: Replace this type! /// Type alias for a container containing bytes. /* Required to support standard libraries without a generic implementation for * `std::char_traits`. @@ -306,27 +306,14 @@ using bytes = std::conditional< has_generic_bytes_char_traits, std::basic_string, std::basic_string>::type; -// C++20: Change this type. -/// Type alias for a view of bytes. -/* Required to support standard libraries without a generic implementation for - * `std::char_traits`. - * @warn Will change to `std::span` in the next major release. - */ -using bytes_view = std::conditional< - has_generic_bytes_char_traits, std::basic_string_view, - std::basic_string_view>::type; - #include "pqxx/internal/ignore-deprecated-post.hxx" -/// Cast binary data to a type that libpqxx will recognise as binary. -/** There are many different formats for storing binary data in memory. You - * may have yours as a `std::string`, or a `std::vector`, or one of - * many other types. In libpqxx we commend a container of `std::byte`. - * - * For libpqxx to recognise your data as binary, we recommend using a - * `pqxx::bytes`, or a `pqxx::bytes_view`; but any contiguous block of - * `std::byte` should do. +/// Cast binary data to libpqxx's standard "view on binary data." +/** There are many different formats for passing a reference to binary data in + * memory. There's `std::string_view`, or a `std::vector`, or one of + * many other types. In libpqxx we recommend `std::span`, but + * thanks to this conversion functions, most of these types should work. * * Use `binary_cast` as a convenience helper to cast your data as a * `pqxx::bytes_view`. @@ -334,23 +321,15 @@ using bytes_view = std::conditional< * @warning You must keep the storage holding the actual data alive for as * long as you might use this function's return value. */ -template inline bytes_view binary_cast(TYPE const &data) +template +inline bytes_view binary_cast(TYPE const &data) { - static_assert(sizeof(value_type) == 1); - // C++20: Use std::as_bytes. - return { - reinterpret_cast( - const_cast const *>( - std::data(data))), - std::size(data)}; + using item_t = value_type; + return std::as_bytes(std::span{std::data(data), std::size(data)}); } -/// A type one byte in size. -template -concept char_sized = (sizeof(CHAR) == 1); - - +// XXX: Write separate tests for binary_cast. /// Construct a type that libpqxx will recognise as binary. /** Takes a data pointer and a size, without being too strict about their * types, and constructs a `pqxx::bytes_view` pointing to the same data. @@ -361,10 +340,7 @@ concept char_sized = (sizeof(CHAR) == 1); template bytes_view binary_cast(CHAR const *data, SIZE size) { - static_assert(sizeof(CHAR) == 1); - return { - reinterpret_cast(data), - check_cast(size, "binary data size")}; + return binary_cast(std::span{data, check_cast(size)}); } diff --git a/include/pqxx/zview.hxx b/include/pqxx/zview.hxx index 94083f686..fdda8ac39 100644 --- a/include/pqxx/zview.hxx +++ b/include/pqxx/zview.hxx @@ -11,6 +11,7 @@ #ifndef PQXX_H_ZVIEW #define PQXX_H_ZVIEW +#include #include #include #include @@ -52,6 +53,9 @@ public: {} /// Explicitly promote a `string_view` to a `zview`. + /** @warning This is not just a type conversion. It's the caller making a + * promise that the string is zero-terminated. + */ explicit constexpr zview(std::string_view other) noexcept : std::string_view{other} {} @@ -91,6 +95,11 @@ public: constexpr zview(char const (&literal)[size]) : zview(literal, size - 1) {} +#if !defined(WIN32) + /// Construct a `zview` from a `std::filesystem::path`. + zview(std::filesystem::path p) : zview(p.c_str()) {} +#endif // WIN32 + /// Either a null pointer, or a zero-terminated text buffer. [[nodiscard]] constexpr char const *c_str() const & noexcept { diff --git a/pqxx_cxx_feature_checks.ac b/pqxx_cxx_feature_checks.ac index 4ffe172a3..9f9f74d8e 100644 --- a/pqxx_cxx_feature_checks.ac +++ b/pqxx_cxx_feature_checks.ac @@ -59,16 +59,6 @@ AC_COMPILE_IFELSE( [Define if this feature is available.]), PQXX_HAVE_MULTIDIM=no) AC_MSG_RESULT($PQXX_HAVE_MULTIDIM) -AC_MSG_CHECKING([PQXX_HAVE_PATH]) -PQXX_HAVE_PATH=yes -AC_COMPILE_IFELSE( - [read_test(PQXX_HAVE_PATH.cxx)], - AC_DEFINE( - [PQXX_HAVE_PATH], - 1, - [Define if this feature is available.]), - PQXX_HAVE_PATH=no) -AC_MSG_RESULT($PQXX_HAVE_PATH) AC_MSG_CHECKING([PQXX_HAVE_POLL]) PQXX_HAVE_POLL=yes AC_COMPILE_IFELSE( diff --git a/src/blob.cxx b/src/blob.cxx index a7d0f5b3e..2f6edaea2 100644 --- a/src/blob.cxx +++ b/src/blob.cxx @@ -162,15 +162,15 @@ std::size_t pqxx::blob::read(bytes &buf, std::size_t size) } -void pqxx::blob::raw_write(std::byte const buf[], std::size_t size) +void pqxx::blob::raw_write(bytes_view data) { if (m_conn == nullptr) throw usage_error{"Attempt to write to a closed binary large object."}; - if (size > chunk_limit) - throw range_error{ - "Writes to a binary large object must be less than 2 GB at once."}; - auto ptr{reinterpret_cast(buf)}; - int const written{lo_write(raw_conn(m_conn), m_fd, ptr, size)}; + auto const sz{std::size(data)}; + if (sz > chunk_limit) + throw range_error{"Write to binary large object exceeds 2GB limit."}; + auto ptr{reinterpret_cast(std::data(data))}; + int const written{lo_write(raw_conn(m_conn), m_fd, ptr, sz)}; if (written < 0) throw failure{ internal::concat("Write to binary large object failed: ", errmsg())}; @@ -305,9 +305,9 @@ std::size_t pqxx::blob::append_to_buf( } -pqxx::oid pqxx::blob::from_file(dbtransaction &tx, char const path[]) +pqxx::oid pqxx::blob::from_file(dbtransaction &tx, zview path) { - auto id{lo_import(raw_conn(tx), path)}; + auto id{lo_import(raw_conn(tx), path.c_str())}; if (id == 0) throw failure{internal::concat( "Could not import '", path, "' as a binary large object: ", errmsg(tx))}; @@ -315,9 +315,9 @@ pqxx::oid pqxx::blob::from_file(dbtransaction &tx, char const path[]) } -pqxx::oid pqxx::blob::from_file(dbtransaction &tx, char const path[], oid id) +pqxx::oid pqxx::blob::from_file(dbtransaction &tx, zview path, oid id) { - auto actual_id{lo_import_with_oid(raw_conn(tx), path, id)}; + auto actual_id{lo_import_with_oid(raw_conn(tx), path.c_str(), id)}; if (actual_id == 0) throw failure{internal::concat( "Could not import '", path, "' as binary large object ", id, ": ", @@ -326,9 +326,9 @@ pqxx::oid pqxx::blob::from_file(dbtransaction &tx, char const path[], oid id) } -void pqxx::blob::to_file(dbtransaction &tx, oid id, char const path[]) +void pqxx::blob::to_file(dbtransaction &tx, oid id, zview path) { - if (lo_export(raw_conn(tx), id, path) < 0) + if (lo_export(raw_conn(tx), id, path.c_str()) < 0) throw failure{internal::concat( "Could not export binary large object ", id, " to file '", path, "': ", errmsg(tx))}; diff --git a/test/unit/test_blob.cxx b/test/unit/test_blob.cxx index fb95767e4..e11103d1e 100644 --- a/test/unit/test_blob.cxx +++ b/test/unit/test_blob.cxx @@ -1,4 +1,5 @@ #include +#include #include #include @@ -587,10 +588,7 @@ void test_blob_close_leaves_blob_unusable() void test_blob_accepts_std_filesystem_path() { -#if defined(PQXX_HAVE_PATH) && !defined(_WIN32) - // A bug in gcc 8's ~std::filesystem::path() causes a run-time crash. -# if !defined(__GNUC__) || (__GNUC__ > 8) - +#if !defined(_WIN32) char const temp_file[] = "blob-test-filesystem-path.tmp"; pqxx::bytes const data{std::byte{'4'}, std::byte{'2'}}; @@ -603,9 +601,7 @@ void test_blob_accepts_std_filesystem_path() auto id{pqxx::blob::from_file(tx, path)}; pqxx::blob::to_buf(tx, id, buf, 10); PQXX_CHECK_EQUAL(buf, data, "Wrong data from blob::from_file()."); - -# endif -#endif +#endif // WIN32 }