From 5e9c78c349375eac3d60fd9d95a69d5fca649c3d Mon Sep 17 00:00:00 2001 From: Andrew Kane Date: Sun, 23 Nov 2025 17:31:18 -0800 Subject: [PATCH 1/6] Added support for libpqxx 8 --- CMakeLists.txt | 6 +-- include/pgvector/pqxx.hpp | 90 +++++++++++++-------------------------- test/pqxx_test.cpp | 4 +- 3 files changed, 35 insertions(+), 65 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e109dc2..6e80a89 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-rc1) 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 1e6a580..aaaf341 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"); } @@ -46,18 +42,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 { @@ -66,16 +56,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"); } @@ -91,18 +77,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 { @@ -111,16 +91,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"); } @@ -130,7 +106,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"); } @@ -149,8 +125,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"); @@ -164,12 +140,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(); @@ -181,25 +152,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 { diff --git a/test/pqxx_test.cpp b/test/pqxx_test.cpp index 5f1c774..25699d1 100644 --- a/test/pqxx_test.cpp +++ b/test/pqxx_test.cpp @@ -116,8 +116,8 @@ void test_stream_to(pqxx::connection &conn) { pqxx::nontransaction tx(conn); auto stream = pqxx::stream_to::table(tx, {"items"}, {"embedding"}); - stream << pgvector::Vector({1, 2, 3}); - stream << pgvector::Vector({4, 5, 6}); + stream.write_values(pgvector::Vector({1, 2, 3})); + stream.write_values(pgvector::Vector({4, 5, 6})); stream.complete(); pqxx::result res = tx.exec("SELECT embedding FROM items ORDER BY id"); assert(res[0][0].as() == "[1,2,3]"); From 85e75c6eca0dcf0908df408ecec3fd6af14aa49f Mon Sep 17 00:00:00 2001 From: Andrew Kane Date: Sun, 23 Nov 2025 17:32:39 -0800 Subject: [PATCH 2/6] Fixed CI --- .github/workflows/build.yml | 4 ---- 1 file changed, 4 deletions(-) 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 From ba0e53e8681116bc8a9751bbf21e0703300527cb Mon Sep 17 00:00:00 2001 From: Andrew Kane Date: Sat, 6 Dec 2025 09:15:27 -0800 Subject: [PATCH 3/6] Updated libpqxx --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e80a89..12367ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ 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 8.0.0-rc1) + FetchContent_Declare(libpqxx GIT_REPOSITORY https://github.com/jtv/libpqxx.git GIT_TAG 8.0.0-rc2) 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) From 48c44ea10d3496769f48a6ba5cfe4ac50353f264 Mon Sep 17 00:00:00 2001 From: Andrew Kane Date: Sun, 21 Dec 2025 13:20:14 -0800 Subject: [PATCH 4/6] Updated libpqxx --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 12367ee..9fccab3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ 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 8.0.0-rc2) + FetchContent_Declare(libpqxx GIT_REPOSITORY https://github.com/jtv/libpqxx.git GIT_TAG 8.0.0-rc3) 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) From d729873fcaa3c892e70e88e4de962a69f5af047b Mon Sep 17 00:00:00 2001 From: Andrew Kane Date: Sat, 24 Jan 2026 14:17:01 -0800 Subject: [PATCH 5/6] Updated libpqxx --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fccab3..2dfff69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ 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 8.0.0-rc3) + FetchContent_Declare(libpqxx GIT_REPOSITORY https://github.com/jtv/libpqxx.git GIT_TAG 8.0.0-rc4) 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) From 2bcf4bf1d01fd95867ed5024297af1c158ed5b09 Mon Sep 17 00:00:00 2001 From: Andrew Kane Date: Tue, 10 Feb 2026 15:03:45 -0800 Subject: [PATCH 6/6] Updated libpqxx --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2dfff69..39f8f47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ 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 8.0.0-rc4) + 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)