diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66f0d83..eb97d3c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,10 +16,6 @@ jobs: make sudo make install - - run: cmake -S . -B build -DBUILD_TESTING=ON -DCMAKE_CXX_STANDARD=17 - - run: cmake --build build - - run: build/test - - run: cmake -S . -B build -DBUILD_TESTING=ON -DCMAKE_CXX_STANDARD=20 - run: cmake --build build - run: build/test diff --git a/CMakeLists.txt b/CMakeLists.txt index e109dc2..39f8f47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ include(GNUInstallDirs) add_library(pgvector INTERFACE) add_library(pgvector::pgvector ALIAS pgvector) -target_compile_features(pgvector INTERFACE cxx_std_17) +target_compile_features(pgvector INTERFACE cxx_std_20) target_include_directories( pgvector @@ -26,13 +26,13 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) if(BUILD_TESTING) include(FetchContent) - FetchContent_Declare(libpqxx GIT_REPOSITORY https://github.com/jtv/libpqxx.git GIT_TAG 7.10.2) + FetchContent_Declare(libpqxx GIT_REPOSITORY https://github.com/jtv/libpqxx.git GIT_TAG 8.0.0-rc5) FetchContent_MakeAvailable(libpqxx) add_executable(test test/halfvec_test.cpp test/main.cpp test/pqxx_test.cpp test/sparsevec_test.cpp test/vector_test.cpp) target_link_libraries(test PRIVATE libpqxx::pqxx pgvector::pgvector) if(NOT MSVC) - target_compile_options(test PRIVATE -Wall -Wextra -Wpedantic -Werror) + target_compile_options(test PRIVATE -Wall -Wextra -Wpedantic -Werror -Wno-unused-parameter) endif() endif() endif() diff --git a/include/pgvector/pqxx.hpp b/include/pgvector/pqxx.hpp index a6c84aa..7dcc58f 100644 --- a/include/pgvector/pqxx.hpp +++ b/include/pgvector/pqxx.hpp @@ -21,16 +21,12 @@ /// @cond namespace pqxx { -template <> inline std::string_view const type_name{"vector"}; +template <> inline constexpr std::string_view name_type() noexcept { return "vector"; }; template <> struct nullness : no_null {}; template <> struct string_traits { - static constexpr bool converts_to_string{true}; - - static constexpr bool converts_from_string{true}; - - static pgvector::Vector from_string(std::string_view text) { + static pgvector::Vector from_string(std::string_view text, ctx c = {}) { if (text.size() < 2 || text.front() != '[' || text.back() != ']') { throw conversion_error("Malformed vector literal"); } @@ -48,18 +44,12 @@ template <> struct string_traits { return pgvector::Vector(result); } - static zview to_buf(char* begin, char* end, const pgvector::Vector& value) { - char *const next = into_buf(begin, end, value); - return zview{begin, next - begin - 1}; - } - - static char* into_buf(char* begin, char* end, const pgvector::Vector& value) { - auto ret = string_traits>::into_buf( - begin, end, static_cast>(value)); + static std::string_view to_buf(std::span buf, const pgvector::Vector& value, ctx c = {}) { + auto len = pqxx::into_buf(buf, static_cast>(value), c); // replace array brackets - *begin = '['; - *(ret - 2) = ']'; - return ret; + buf[0] = '['; + buf[len - 1] = ']'; + return {std::data(buf), len}; } static size_t size_buffer(const pgvector::Vector& value) noexcept { @@ -68,16 +58,12 @@ template <> struct string_traits { } }; -template <> inline std::string_view const type_name{"halfvec"}; +template <> inline constexpr std::string_view name_type() noexcept { return "halfvec"; }; template <> struct nullness : no_null {}; template <> struct string_traits { - static constexpr bool converts_to_string{true}; - - static constexpr bool converts_from_string{true}; - - static pgvector::HalfVector from_string(std::string_view text) { + static pgvector::HalfVector from_string(std::string_view text, ctx c = {}) { if (text.size() < 2 || text.front() != '[' || text.back() != ']') { throw conversion_error("Malformed halfvec literal"); } @@ -95,18 +81,12 @@ template <> struct string_traits { return pgvector::HalfVector(result); } - static zview to_buf(char* begin, char* end, const pgvector::HalfVector& value) { - char *const next = into_buf(begin, end, value); - return zview{begin, next - begin - 1}; - } - - static char* into_buf(char* begin, char* end, const pgvector::HalfVector& value) { - auto ret = string_traits>::into_buf( - begin, end, static_cast>(value)); + static std::string_view to_buf(std::span buf, const pgvector::HalfVector& value, ctx c = {}) { + auto len = pqxx::into_buf(buf, static_cast>(value), c); // replace array brackets - *begin = '['; - *(ret - 2) = ']'; - return ret; + buf[0] = '['; + buf[len - 1] = ']'; + return {std::data(buf), len}; } static size_t size_buffer(const pgvector::HalfVector& value) noexcept { @@ -115,16 +95,12 @@ template <> struct string_traits { } }; -template <> inline std::string_view const type_name{"sparsevec"}; +template <> inline constexpr std::string_view name_type() noexcept { return "sparsevec"; }; template <> struct nullness : no_null {}; template <> struct string_traits { - static constexpr bool converts_to_string{true}; - - static constexpr bool converts_from_string{true}; - - static pgvector::SparseVector from_string(std::string_view text) { + static pgvector::SparseVector from_string(std::string_view text, ctx c = {}) { if (text.size() < 4 || text.front() != '{') { throw conversion_error("Malformed sparsevec literal"); } @@ -134,7 +110,7 @@ template <> struct string_traits { throw conversion_error("Malformed sparsevec literal"); } - int dimensions = string_traits::from_string(text.substr(n + 2)); + int dimensions = string_traits::from_string(text.substr(n + 2), c); if (dimensions < 0) { throw conversion_error("Dimensions cannot be negative"); } @@ -153,8 +129,8 @@ template <> struct string_traits { throw conversion_error("Malformed sparsevec literal"); } - int index = string_traits::from_string(substr.substr(0, ne)); - float value = string_traits::from_string(substr.substr(ne + 1)); + int index = string_traits::from_string(substr.substr(0, ne), c); + float value = string_traits::from_string(substr.substr(ne + 1), c); if (index < 1) { throw conversion_error("Index out of bounds"); @@ -168,12 +144,7 @@ template <> struct string_traits { return pgvector::SparseVector(dimensions, indices, values); } - static zview to_buf(char* begin, char* end, const pgvector::SparseVector& value) { - char *const next = into_buf(begin, end, value); - return zview{begin, next - begin - 1}; - } - - static char* into_buf(char* begin, char* end, const pgvector::SparseVector& value) { + static std::string_view to_buf(std::span buf, const pgvector::SparseVector& value, ctx c = {}) { int dimensions = value.dimensions(); auto indices = value.indices(); auto values = value.values(); @@ -185,25 +156,24 @@ template <> struct string_traits { throw conversion_overrun{"sparsevec cannot have more than 16000 dimensions"}; } - char *here = begin; - *here++ = '{'; + size_t here = 0; + buf[here++] = '{'; for (size_t i = 0; i < nnz; i++) { if (i != 0) { - *here++ = ','; + buf[here++] = ','; } - here = string_traits::into_buf(here, end, indices[i] + 1) - 1; - *here++ = ':'; - here = string_traits::into_buf(here, end, values[i]) - 1; + here += pqxx::into_buf(buf.subspan(here), indices[i] + 1, c); + buf[here++] = ':'; + here += pqxx::into_buf(buf.subspan(here), values[i], c); } - *here++ = '}'; - *here++ = '/'; - here = string_traits::into_buf(here, end, dimensions) - 1; - *here++ = '\0'; + buf[here++] = '}'; + buf[here++] = '/'; + here += pqxx::into_buf(buf.subspan(here), dimensions, c); - return here; + return {std::data(buf), here}; } static size_t size_buffer(const pgvector::SparseVector& value) noexcept {