[
  {
    "path": ".clang-format",
    "content": "# The Art of C++\n# https://github.com/taocpp\n\n# Copyright (c) 2016-2026 Dr. Colin Hirsch and Daniel Frey\n# Distributed under the Boost Software License, Version 1.0.\n# (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n# This is our official .clang-format style for https://github.com/taocpp\n#\n# clang-format -i -style=file $(find . -name '[^.]*.[hc]pp')\n\nLanguage: Cpp\nStandard: Latest\n\nAccessModifierOffset: -3\nAlignAfterOpenBracket: Align\nAlignConsecutiveAssignments: false\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlinesLeft: false\nAlignOperands: true\nAlignTrailingComments: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortBlocksOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: Empty\nAllowShortIfStatementsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nAlwaysBreakTemplateDeclarations: Yes\nBinPackArguments: false\nBinPackParameters: false\nBraceWrapping:\n    AfterClass: true\n    AfterControlStatement: false\n    AfterEnum : true\n    AfterFunction : true\n    AfterNamespace : true\n    AfterStruct : true\n    AfterUnion : true\n    AfterExternBlock: true\n    BeforeCatch : true\n    BeforeElse : true\n    IndentBraces : false\n    SplitEmptyFunction: false\n    SplitEmptyRecord: false\n    SplitEmptyNamespace: false\nBreakBeforeBinaryOperators: All\nBreakBeforeBraces: Custom\nBreakBeforeTernaryOperators: false\nBreakConstructorInitializers: BeforeColon\nBreakInheritanceList: BeforeColon\nBreakStringLiterals: false\nColumnLimit: 0\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nConstructorInitializerIndentWidth: 3\nContinuationIndentWidth: 3\nCpp11BracedListStyle: false\nDerivePointerAlignment: false\nDisableFormat: false\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: true\nIncludeBlocks: Preserve\nIndentCaseLabels: true\nIndentPPDirectives: None\nIndentWidth: 3\nIndentWrappedFunctionNames: false\nKeepEmptyLinesAtTheStartOfBlocks: false\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: All\nPointerAlignment: Left\nReflowComments: false\nSortIncludes: true\nSortUsingDeclarations: false\nSpaceAfterCStyleCast: false\nSpaceAfterTemplateKeyword: false\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCpp11BracedList: false\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: Never\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 2\nSpacesInAngles: true\nSpacesInCStyleCastParentheses: false\nSpacesInParentheses: true\nSpacesInSquareBrackets: true\nTabWidth: 8\nUseTab: Never\n"
  },
  {
    "path": ".clang-tidy",
    "content": "# The Art of C++\n# https://github.com/taocpp\n\n# Copyright (c) 2016-2026 Dr. Colin Hirsch and Daniel Frey\n# Distributed under the Boost Software License, Version 1.0.\n# (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n# Note: The misc-include-cleaner is generally useful,\n# but produces false positives with Oid/InvalidOid and libpq-fe.h.\n# For that reason it is disabled, but it should be enabled\n# manually from time to time.\n\nChecks: >-\n  bugprone-*,\n  -bugprone-easily-swappable-parameters,\n  cppcoreguidelines-slicing,\n  cppcoreguidelines-special-member-functions,\n  google-build-explicit-make-pair,\n  google-build-namespaces,\n  google-default-arguments,\n  google-global-names-in-headers,\n  google-readability-casting,\n  llvm-*,\n  misc-*,\n  -misc-include-cleaner,\n  -misc-non-private-member-variables-in-classes,\n  -misc-unused-alias-decls,\n  modernize-*,\n  -modernize-avoid-c-arrays,\n  -modernize-concat-nested-namespaces,\n  -modernize-raw-string-literal,\n  performance-*,\n  readability-*,\n  -readability-avoid-const-params-in-decls,\n  -readability-function-cognitive-complexity,\n  -readability-identifier-length,\n  -readability-magic-numbers,\n  -readability-non-const-parameter,\n\nCheckOptions:\n  - { key: readability-identifier-naming.ClassCase,     value: lower_case }\n  - { key: readability-identifier-naming.FunctionCase,  value: lower_case }\n  - { key: readability-identifier-naming.ParameterCase, value: lower_case }\n  - { key: readability-identifier-naming.StructCase,    value: lower_case }\n  - { key: readability-identifier-naming.VariableCase,  value: lower_case }\n\nWarningsAsErrors: '*'\n"
  },
  {
    "path": ".github/conan/conanfile.py",
    "content": "from conan import ConanFile\nfrom conan.tools.cmake import CMakeDeps, CMakeToolchain, cmake_layout\n\nclass TaopqRequirements(ConanFile):\n    settings = \"os\", \"compiler\", \"build_type\", \"arch\"\n    default_options = {\n        \"libpq/*:with_openssl\": False,\n        \"libpq/*:with_icu\": False,\n        \"libpq/*:with_zlib\": False,\n        \"libpq/*:with_zstd\": False,\n        \"libpq/*:with_libxml2\": False,\n        \"libpq/*:with_lz4\": False,\n        \"libpq/*:with_xslt\": False,\n        \"libpq/*:with_readline\": False\n    }\n\n    def layout(self):\n        cmake_layout(self)\n\n    def requirements(self):\n        self.requires(\"libpq/[*]\")\n\n    def generate(self):\n        tc = CMakeToolchain(self)\n        tc.generate()\n\n        deps = CMakeDeps(self)\n        deps.set_property(\"libpq\", \"cmake_file_name\", \"PostgreSQL\")\n        deps.set_property(\"libpq\", \"cmake_target_name\", \"PostgreSQL::PostgreSQL\")\n        deps.set_property(\"libpq\", \"cmake_additional_variables_prefixes\", [\"PostgreSQL\",])\n        deps.generate()\n"
  },
  {
    "path": ".github/conan/profiles/clangcl",
    "content": "[settings]\nos=Windows\narch=x86_64\nbuild_type=Release\ncompiler=clang\ncompiler.version=19\ncompiler.cppstd=20\ncompiler.runtime=dynamic\ncompiler.runtime_type=Release\ncompiler.runtime_version=v144\n\nlibpq/*:compiler=msvc\nlibpq/*:compiler.version=194\nlibpq/*:compiler.cppstd=20\nlibpq/*:compiler.runtime=dynamic\nlibpq/*:compiler.runtime_type=Release\n\n[conf]\ntools.cmake.cmaketoolchain:generator=Ninja\ntools.meson.mesontoolchain:backend=ninja\ntools.build:compiler_executables={\"c\": \"clang-cl.exe\", \"cpp\": \"clang-cl.exe\"}\nlibpq/*:tools.build:compiler_executables={\"c\": \"cl.exe\", \"cpp\": \"cl.exe\"}\n"
  },
  {
    "path": ".github/conan/profiles/msvc",
    "content": " [settings]\narch=x86_64\nbuild_type=Release\ncompiler=msvc\ncompiler.cppstd=20\ncompiler.runtime=dynamic\ncompiler.runtime_type=Release\ncompiler.version=194\nos=Windows\n\n[conf]\ntools.cmake.cmaketoolchain:generator=Ninja\ntools.meson.mesontoolchain:backend=ninja\n"
  },
  {
    "path": ".github/workflows/clang-analyze.yml",
    "content": "name: clang-analyze\n\non:\n  push:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n  pull_request:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n\njobs:\n  clang-analyze:\n    runs-on: ubuntu-24.04\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: clang-tools libpq-dev\n        version: 1.0\n\n    - name: Configure CMake Project\n      run: scan-build cmake --preset unixlike-release-dev\n\n    - name: Build project files\n      run: scan-build cmake --build --preset unixlike-release-dev\n"
  },
  {
    "path": ".github/workflows/clang-format.yml",
    "content": "name: clang-format\n\non:\n  push:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n  pull_request:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n\njobs:\n  clang-format:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v5\n    - uses: DoozyX/clang-format-lint-action@v0.20\n      with:\n        extensions: 'hpp,cpp'\n"
  },
  {
    "path": ".github/workflows/clang-tidy.yml",
    "content": "name: clang-tidy\n\non:\n  push:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n  pull_request:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n\njobs:\n  clang-tidy:\n    runs-on: ubuntu-24.04\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: libpq-dev clang-tidy\n        version: 3.0\n\n    - name: Configure CMake Project\n      run: cmake --preset unixlike-debug-dev -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF\n\n    - name: Build project files\n      run: cmake --build --preset unixlike-debug-dev\n\n    - name: Run clang-tidy\n      run: run-clang-tidy -p build/unixlike-debug-dev/\n"
  },
  {
    "path": ".github/workflows/code-coverage.yml",
    "content": "name: code-coverage\n\non:\n  push:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n  pull_request:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n\njobs:\n  code-coverage:\n    runs-on: ubuntu-24.04\n\n    services:\n      postgres:\n        image: postgres:latest\n        env:\n          POSTGRES_DB: postgres\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_USER: postgres\n        ports:\n          - 5432:5432\n        # Set health checks to wait until postgres has started\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n\n    env:\n      TAOPQ_TEST_DATABASE: host=localhost dbname=postgres user=postgres password=postgres\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: libpq-dev\n        version: 1.0\n\n    - name: Exclude example folder from code coverage\n      run: |\n        printf \"ignore:\\n  - example/**\\n\" >> .codecov.yml\n\n    - name: Configure CMake Project\n      run: cmake --preset unixlike-release-dev -DCMAKE_CXX_FLAGS=\"-coverage\" -DBUILD_EXAMPLES=OFF\n\n    - name: Build project files\n      run: cmake --build --preset unixlike-release-dev\n\n    - name: Run tests\n      run: cmake --build --preset unixlike-release-dev --target test\n\n    - uses: codecov/codecov-action@v5\n      with:\n        token: ${{ secrets.CODECOV_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/linux.yml",
    "content": "name: Linux\n\non:\n  push:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n  pull_request:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n\njobs:\n  linux-next:\n    strategy:\n      matrix:\n        compiler:\n          - g++-13\n          - g++-14\n          - g++-15\n          - g++-16\n          - clang++-18\n\n    runs-on: ubuntu-24.04\n\n    services:\n      postgres:\n        image: postgres:latest\n        env:\n          POSTGRES_DB: postgres\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_USER: postgres\n        ports:\n          - 5432:5432\n        # Set health checks to wait until postgres has started\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n\n    env:\n      CXX: ${{ matrix.compiler }}\n      TAOPQ_TEST_DATABASE: host=localhost dbname=postgres user=postgres password=postgres\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: libpq-dev libc++-dev\n        version: 2.0\n\n    - name: Install GCC-15\n      if: matrix.compiler == 'g++-15'\n      run: |\n        sudo add-apt-repository ppa:ubuntu-toolchain-r/test\n        sudo apt-get update\n        sudo apt-get install -y g++-15\n\n    - name: Install GCC-16\n      if: matrix.compiler == 'g++-16'\n      run: |\n        sudo add-apt-repository ppa:ubuntu-toolchain-r/test\n        sudo apt-get update\n        sudo apt-get install -y g++-16\n\n    - name: Configure CMake Project\n      run: cmake --preset unixlike-release-dev -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/taopq-install\n\n    - name: Build project files\n      run: cmake --build --preset unixlike-release-dev\n\n    - name: Run tests\n      run: cmake --build --preset unixlike-release-dev --target test\n\n    - name: Run examples\n      run: cmake --build --preset unixlike-release-dev --target examples\n      env:\n        PGDATABASE: ${{ env.TAOPQ_TEST_DATABASE }}\n\n    - name: Install Taocpp TaoPQ\n      run: cmake --build --preset unixlike-release-dev --target install\n\n  linux-gcc-old-abi:\n    runs-on: ubuntu-24.04\n\n    services:\n      postgres:\n        image: postgres:latest\n        env:\n          POSTGRES_DB: postgres\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_USER: postgres\n        ports:\n          - 5432:5432\n        # Set health checks to wait until postgres has started\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n\n    env:\n      CXX: g++\n      TAOPQ_TEST_DATABASE: host=localhost dbname=postgres user=postgres password=postgres\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: libpq-dev libc++-dev\n        version: 2.0\n\n    - name: Configure CMake Project\n      run: cmake --preset unixlike-release-dev -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/taopq-install -DCMAKE_CXX_FLAGS=\"-D_GLIBCXX_USE_CXX11_ABI=0\"\n\n    - name: Build project files\n      run: cmake --build --preset unixlike-release-dev\n\n    - name: Run tests\n      run: cmake --build --preset unixlike-release-dev --target test\n\n  linux-clang-extra:\n    strategy:\n      matrix:\n        flags: [\"-stdlib=libc++\",\"-fms-extensions\"]\n\n    runs-on: ubuntu-24.04\n\n    services:\n      postgres:\n        image: postgres:latest\n        env:\n          POSTGRES_DB: postgres\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_USER: postgres\n        ports:\n          - 5432:5432\n        # Set health checks to wait until postgres has started\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n\n    env:\n      CXX: clang++\n      TAOPQ_TEST_DATABASE: host=localhost dbname=postgres user=postgres password=postgres\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: libpq-dev libc++-dev\n        version: 2.0\n\n    - name: Configure CMake Project\n      run: cmake --preset unixlike-release-dev -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/taopq-install -DCMAKE_CXX_FLAGS=\"${{ matrix.flags }}\"\n\n    - name: Build project files\n      run: cmake --build --preset unixlike-release-dev\n\n    - name: Run tests\n      run: cmake --build --preset unixlike-release-dev --target test\n\n  linux-makefile:\n    runs-on: ubuntu-24.04\n\n    services:\n      postgres:\n        image: postgres:latest\n        env:\n          POSTGRES_DB: postgres\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_USER: postgres\n        ports:\n          - 5432:5432\n        # Set health checks to wait until postgres has started\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n\n    env:\n      TAOPQ_TEST_DATABASE: host=localhost dbname=postgres user=postgres password=postgres\n      CXX: g++-13\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: libpq-dev libc++-dev\n        version: 2.0\n\n    - name: Build project files with Makefile\n      run: make -j$(nproc)\n\n    - name: Run tests with Makefile\n      run: make test\n      env:\n        PGDATABASE: ${{ env.TAOPQ_TEST_DATABASE }}\n"
  },
  {
    "path": ".github/workflows/macos.yml",
    "content": "name: macOS\n\non:\n  push:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n  pull_request:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n\njobs:\n  xcode-macos-15:\n    runs-on: macos-15\n    env:\n      TAOPQ_TEST_DATABASE: host=localhost dbname=postgres user=postgres password=postgres\n\n    steps:\n    - uses: actions/checkout@v5\n    - uses: ikalnytskyi/action-setup-postgres@v8\n    - uses: conan-io/setup-conan@v1\n      with:\n        cache_packages: true\n\n    - name: Install dependencies with Conan\n      run: conan install ${{ github.workspace }}/.github/conan/conanfile.py -s compiler.cppstd=20 --build=missing --lockfile-partial\n\n    - name: Configure CMake Project\n      run: cmake --preset unixlike-release-dev -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/taopq-install -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/.github/conan/build/Release/generators/conan_toolchain.cmake\n\n    - name: Build project files\n      run: cmake --build --preset unixlike-release-dev\n\n    - name: Run tests\n      run: cmake --build --preset unixlike-release-dev --target test\n\n    - name: Run examples\n      run: cmake --build --preset unixlike-release-dev --target examples\n      env:\n        PGDATABASE: ${{ env.TAOPQ_TEST_DATABASE }}\n\n    - name: Install Taocpp TaoPQ\n      run: cmake --build --preset unixlike-release-dev --target install\n"
  },
  {
    "path": ".github/workflows/sanitizer.yml",
    "content": "name: Sanitizer\n\non:\n  push:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n  pull_request:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n\njobs:\n  sanitizer:\n    strategy:\n      matrix:\n        sanitizer: [address, undefined, thread, leak]\n\n    runs-on: ubuntu-24.04\n\n    services:\n      postgres:\n        image: postgres:latest\n        env:\n          POSTGRES_DB: postgres\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_USER: postgres\n        ports:\n          - 5432:5432\n        # Set health checks to wait until postgres has started\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n\n    env:\n      CXX: clang++\n      CC: clang\n      TAOPQ_TEST_DATABASE: host=localhost dbname=postgres user=postgres password=postgres\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - uses: awalsh128/cache-apt-pkgs-action@latest\n      with:\n        packages: libpq-dev\n        version: 1.0\n\n    - name: Configure CMake Project\n      run: cmake --preset unixlike-debug-dev -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/taopq-install -DCMAKE_CXX_FLAGS=\"-fsanitize=${{ matrix.sanitizer }}\"\n\n    - name: Build project files\n      run: cmake --build --preset unixlike-debug-dev\n\n    - name: Run tests\n      run: cmake --build --preset unixlike-debug-dev --target test\n"
  },
  {
    "path": ".github/workflows/windows.yml",
    "content": "name: Windows\n\non:\n  push:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n  pull_request:\n    paths-ignore:\n      - 'README.md'\n      - 'doc/**'\n\njobs:\n  vs2022:\n    runs-on: windows-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        cxx: [ \"msvc\", \"clangcl\" ]\n\n    env:\n      TAOPQ_TEST_DATABASE: host=localhost dbname=postgres user=postgres password=postgres\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - name: Set up PostgreSQL\n      uses: ikalnytskyi/action-setup-postgres@v8\n\n    - name: Set up MSVC dev cmd (x64)\n      uses: ilammy/msvc-dev-cmd@v1\n      with:\n        arch: x64\n\n    - name: Set up Conan\n      uses: conan-io/setup-conan@v1\n      with:\n        cache_packages: true\n        config_urls: .github/conan\n\n    - name: Install dependencies with Conan MSVC\n      run: conan install ${{ github.workspace }}/.github/conan/conanfile.py --build=missing -pr ${{ matrix.cxx }} --lockfile-partial\n\n    - name: CMake Configure\n      shell: pwsh\n      run: cmake --preset ${{ matrix.cxx }}-release-dev -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/taopq-install -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/.github/conan/build/Release/generators/conan_toolchain.cmake\n\n    - name: Build project files\n      run: cmake --build --preset ${{ matrix.cxx }}-release-dev\n\n    - name: Run tests\n      run: cmake --build --preset ${{ matrix.cxx }}-release-dev --target test\n\n    - name: Run examples\n      run: cmake --build --preset ${{ matrix.cxx }}-release-dev --target examples\n      env:\n        PGDATABASE: ${{ env.TAOPQ_TEST_DATABASE }}\n\n    - name: Install Taocpp TaoPQ\n      run: cmake --build --preset ${{ matrix.cxx }}-release-dev --target install\n"
  },
  {
    "path": ".gitignore",
    "content": "*~\nbuild\ndummy.txt\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\n\n# Read version from version.hpp\nfile(READ \"${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/version.hpp\" taopq_VERSION_DATA)\nstring(REGEX MATCH \"#define TAO_PQ_VERSION \\\"([^\\\"]+)\\\"\" _ ${taopq_VERSION_DATA})\nset(taopq_VERSION \"${CMAKE_MATCH_1}\")\n\nproject(taopq\n  VERSION ${taopq_VERSION}\n  LANGUAGES CXX\n  DESCRIPTION \"A lightweight C++ client library for accessing a PostgreSQL database\"\n  HOMEPAGE_URL \"https://github.com/taocpp/taopq\"\n)\n\noption(BUILD_EXAMPLES \"Build taopq examples\" ON)\n\nfind_package(PostgreSQL REQUIRED)\n\nadd_library(${PROJECT_NAME})\nadd_library(taocpp::taopq ALIAS ${PROJECT_NAME})\n\ntarget_sources(${PROJECT_NAME}\n  PRIVATE\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/connection.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/connection_pool.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/exception.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/field.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/internal/demangle.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/internal/poll.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/internal/strtox.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/large_object.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/parameter_traits.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/pipeline.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/result.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/result_traits.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/result_traits_array.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/row.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/table_field.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/table_reader.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/table_row.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/table_writer.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/transaction.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/pq/transaction_base.cpp\n  PUBLIC FILE_SET HEADERS\n  BASE_DIRS include\n  FILES\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/access_mode.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/binary.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/bind.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/connection.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/connection_pool.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/connection_status.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/exception.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/field.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/aggregate.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/demangle.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/exclusive_scan.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/format_as.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/from_chars.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/gen.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/parameter_traits_helper.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/poll.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/pool.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/resize_uninitialized.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/strtox.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/unreachable.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/internal/zsv.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/is_aggregate.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/is_array.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/isolation_level.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/large_object.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/log.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/notification.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/null.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/oid.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/parameter.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/parameter_traits.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/parameter_traits_aggregate.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/parameter_traits_array.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/parameter_traits_optional.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/parameter_traits_pair.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/parameter_traits_tuple.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/pipeline.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/pipeline_status.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/poll.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/result.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/result_status.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/result_traits.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/result_traits_aggregate.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/result_traits_array.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/result_traits_optional.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/result_traits_pair.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/result_traits_tuple.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/row.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/table_field.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/table_reader.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/table_row.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/table_writer.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/transaction.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/transaction_base.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/transaction_status.hpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/include/tao/pq/version.hpp\n)\n\ntarget_include_directories(${PROJECT_NAME}\n  PUBLIC\n    $<INSTALL_INTERFACE:include>\n    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>\n  PRIVATE\n    ${CMAKE_CURRENT_SOURCE_DIR}/src\n)\n\ntarget_link_libraries(${PROJECT_NAME} PUBLIC PostgreSQL::PostgreSQL)\nif(WIN32)\n  target_link_libraries(${PROJECT_NAME} PUBLIC ws2_32)\nendif()\n\ntarget_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)\n\nset_target_properties(${PROJECT_NAME} PROPERTIES\n  OUTPUT_NAME taopq\n  VERSION ${PROJECT_VERSION}\n  SOVERSION ${PROJECT_VERSION_MAJOR}\n  CXX_VISIBILITY_PRESET hidden\n  VISIBILITY_INLINES_HIDDEN ON\n  EXPORT_NAME taopq\n)\n\nif(PROJECT_IS_TOP_LEVEL)\n  include(GNUInstallDirs)\n  include(CMakePackageConfigHelpers)\n\n  install(TARGETS taopq\n    EXPORT taopq-targets\n    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\n    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}\n    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}\n    FILE_SET HEADERS\n  )\n\n  install(FILES LICENSE_1_0.txt DESTINATION ${CMAKE_INSTALL_DOCDIR})\n\n  install(EXPORT taopq-targets\n    FILE ${PROJECT_NAME}Targets.cmake\n    NAMESPACE taocpp::\n    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taopq\n  )\n\n  write_basic_package_version_file(\n    ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake\n    VERSION ${PROJECT_VERSION}\n    COMPATIBILITY SameMajorVersion\n  )\n\n  file(WRITE \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake\"\n\"include(CMakeFindDependencyMacro)\nfind_dependency(PostgreSQL REQUIRED CONFIG)\ninclude(\\\"\\${CMAKE_CURRENT_LIST_DIR}/taopqTargets.cmake\\\")\n\")\n\n  install(FILES\n    \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake\"\n    \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake\"\n    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taopq\n  )\n\n  export(EXPORT taopq-targets\n    FILE \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake\"\n    NAMESPACE taocpp::\n  )\nendif()\n\nif(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)\n  enable_testing()\n  add_subdirectory(test)\nendif()\n\nif(BUILD_EXAMPLES AND PROJECT_IS_TOP_LEVEL)\n  add_subdirectory(example)\nendif()"
  },
  {
    "path": "CMakePresets.json",
    "content": "{\n  \"version\": 6,\n  \"cmakeMinimumRequired\": {\n    \"major\": 3,\n    \"minor\": 21,\n    \"patch\": 0\n  },\n  \"configurePresets\": [\n    {\n      \"name\": \"default\",\n      \"hidden\": true,\n      \"generator\": \"Ninja\",\n      \"binaryDir\": \"${sourceDir}/build/${presetName}\",\n      \"cacheVariables\": {\n        \"CMAKE_EXPORT_COMPILE_COMMANDS\": \"ON\"\n      }\n    },\n    {\n      \"name\": \"dev-flags-gcc-clang\",\n      \"hidden\": true,\n      \"cacheVariables\": {\n        \"CMAKE_CXX_FLAGS\": \"-Wall -Wextra -Wpedantic -Werror -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -Wnull-dereference -Wno-sign-conversion\",\n        \"BUILD_TESTING\": \"ON\",\n        \"BUILD_EXAMPLES\": \"ON\"\n      }\n    },\n    {\n      \"name\": \"dev-flags-msvc\",\n      \"hidden\": true,\n      \"cacheVariables\": {\n        \"CMAKE_CXX_FLAGS\": \"/EHsc /W4 /WX /permissive-\",\n        \"BUILD_TESTING\": \"ON\",\n        \"BUILD_EXAMPLES\": \"ON\"\n      }\n    },\n    {\n      \"name\": \"debug\",\n      \"inherits\": \"default\",\n      \"displayName\": \"Debug\",\n      \"description\": \"Debug build\",\n      \"cacheVariables\": {\n        \"CMAKE_BUILD_TYPE\": \"Debug\"\n      }\n    },\n    {\n      \"name\": \"release\",\n      \"inherits\": \"default\",\n      \"displayName\": \"Release\",\n      \"description\": \"Release build with optimizations\",\n      \"cacheVariables\": {\n        \"CMAKE_BUILD_TYPE\": \"Release\"\n      }\n    },\n    {\n      \"name\": \"unixlike-debug\",\n      \"inherits\": \"debug\",\n      \"displayName\": \"Unix Like Compiler Debug\",\n      \"description\": \"Debug build using GCC or Clang\"\n    },\n    {\n      \"name\": \"unixlike-release\",\n      \"inherits\": \"release\",\n      \"displayName\": \"Unix Like Compiler Release\",\n      \"description\": \"Release build using GCC or Clang\"\n    },\n    {\n      \"name\": \"unixlike-debug-dev\",\n      \"inherits\": [\"debug\", \"dev-flags-gcc-clang\"],\n      \"displayName\": \"Unix Like Compiler Debug (Dev with -Werror)\",\n      \"description\": \"Debug build using Clang or GCC with warnings as errors\"\n    },\n    {\n      \"name\": \"unixlike-release-dev\",\n      \"inherits\": [\"release\", \"dev-flags-gcc-clang\"],\n      \"displayName\": \"Unix Like Compiler Release (Dev with -Werror)\",\n      \"description\": \"Release build using Clang or GCC with warnings as errors\"\n    },\n    {\n      \"name\": \"clangcl-debug\",\n      \"inherits\": \"debug\",\n      \"displayName\": \"MSVC Debug\",\n      \"description\": \"Debug build using MSVC\",\n      \"generator\": \"Ninja\",\n      \"cacheVariables\": {\n        \"CMAKE_C_COMPILER\": \"clang-cl\",\n        \"CMAKE_CXX_COMPILER\": \"clang-cl\"\n      },\n      \"condition\": {\n        \"type\": \"equals\",\n        \"lhs\": \"${hostSystemName}\",\n        \"rhs\": \"Windows\"\n      }\n    },\n    {\n      \"name\": \"clangcl-release\",\n      \"inherits\": \"release\",\n      \"displayName\": \"MSVC Release\",\n      \"description\": \"Ninja\",\n      \"generator\": \"Ninja\",\n      \"cacheVariables\": {\n        \"CMAKE_C_COMPILER\": \"clang-cl\",\n        \"CMAKE_CXX_COMPILER\": \"clang-cl\"\n      },\n      \"condition\": {\n        \"type\": \"equals\",\n        \"lhs\": \"${hostSystemName}\",\n        \"rhs\": \"Windows\"\n      }\n    },\n    {\n      \"name\": \"msvc-debug\",\n      \"inherits\": \"debug\",\n      \"displayName\": \"MSVC Debug\",\n      \"description\": \"Ninja\",\n      \"generator\": \"Ninja\",\n      \"cacheVariables\": {\n        \"CMAKE_C_COMPILER\": \"cl\",\n        \"CMAKE_CXX_COMPILER\": \"cl\"\n      },\n      \"condition\": {\n        \"type\": \"equals\",\n        \"lhs\": \"${hostSystemName}\",\n        \"rhs\": \"Windows\"\n      }\n    },\n    {\n      \"name\": \"msvc-release\",\n      \"inherits\": \"release\",\n      \"displayName\": \"MSVC Release\",\n      \"description\": \"Release build using MSVC\",\n      \"generator\": \"Ninja\",\n      \"cacheVariables\": {\n        \"CMAKE_C_COMPILER\": \"cl\",\n        \"CMAKE_CXX_COMPILER\": \"cl\"\n      },\n      \"condition\": {\n        \"type\": \"equals\",\n        \"lhs\": \"${hostSystemName}\",\n        \"rhs\": \"Windows\"\n      }\n    },\n    {\n      \"name\": \"msvc-debug-dev\",\n      \"inherits\": [\"msvc-debug\", \"dev-flags-msvc\"],\n      \"displayName\": \"MSVC Debug (Dev with /WX)\",\n      \"description\": \"Debug build using MSVC with warnings as errors\"\n    },\n    {\n      \"name\": \"msvc-release-dev\",\n      \"inherits\": [\"msvc-release\", \"dev-flags-msvc\"],\n      \"displayName\": \"MSVC Release (Dev with /WX)\",\n      \"description\": \"Release build using MSVC with warnings as errors\"\n    },\n    {\n      \"name\": \"clangcl-debug-dev\",\n      \"inherits\": [\"clangcl-debug\", \"dev-flags-msvc\"],\n      \"displayName\": \"ClangCL Debug (Dev with /WX)\",\n      \"description\": \"Debug build using ClangCL with warnings as errors\"\n    },\n    {\n      \"name\": \"clangcl-release-dev\",\n      \"inherits\": [\"clangcl-release\", \"dev-flags-msvc\"],\n      \"displayName\": \"ClangCL Release (Dev with /WX)\"\n    }\n  ],\n  \"buildPresets\": [\n    {\n      \"name\": \"debug\",\n      \"configurePreset\": \"debug\",\n      \"displayName\": \"Debug Build\",\n      \"description\": \"Build in Debug mode\"\n    },\n    {\n      \"name\": \"release\",\n      \"configurePreset\": \"release\",\n      \"displayName\": \"Release Build\",\n      \"description\": \"Build in Release mode\"\n    },\n    {\n      \"name\": \"unixlike-debug\",\n      \"configurePreset\": \"unixlike-debug\",\n      \"displayName\": \"Unix Like Compiler Debug Build\"\n    },\n    {\n      \"name\": \"unixlike-release\",\n      \"configurePreset\": \"unixlike-release\",\n      \"displayName\": \"Unix Like Compiler Release Build\"\n    },\n    {\n      \"name\": \"unixlike-debug-dev\",\n      \"configurePreset\": \"unixlike-debug-dev\",\n      \"displayName\": \"Unix Like Compiler Debug Build (Dev)\"\n    },\n    {\n      \"name\": \"unixlike-release-dev\",\n      \"configurePreset\": \"unixlike-release-dev\",\n      \"displayName\": \"Unix Like Compiler Release Build (Dev)\"\n    },\n    {\n      \"name\": \"msvc-debug\",\n      \"configurePreset\": \"msvc-debug\",\n      \"displayName\": \"MSVC Debug Build\"\n    },\n    {\n      \"name\": \"msvc-release\",\n      \"configurePreset\": \"msvc-release\",\n      \"displayName\": \"MSVC Release Build\"\n    },\n    {\n      \"name\": \"msvc-debug-dev\",\n      \"configurePreset\": \"msvc-debug-dev\",\n      \"displayName\": \"MSVC Debug Build (Dev)\"\n    },\n    {\n      \"name\": \"msvc-release-dev\",\n      \"configurePreset\": \"msvc-release-dev\",\n      \"displayName\": \"MSVC Release Build (Dev)\"\n    },\n    {\n      \"name\": \"clangcl-debug\",\n      \"configurePreset\": \"clangcl-debug\",\n      \"displayName\": \"MSVC Clang Debug Build\"\n    },\n    {\n      \"name\": \"clangcl-release\",\n      \"configurePreset\": \"clangcl-release\",\n      \"displayName\": \"MSVC Clang Release Build\"\n    },\n    {\n      \"name\": \"clangcl-debug-dev\",\n      \"configurePreset\": \"clangcl-debug-dev\",\n      \"displayName\": \"ClangCL Debug Build (Dev)\"\n    },\n    {\n      \"name\": \"clangcl-release-dev\",\n      \"configurePreset\": \"clangcl-release-dev\",\n      \"displayName\": \"ClangCL Release Build (Dev)\"\n    }\n  ],\n  \"testPresets\": [\n    {\n      \"name\": \"debug\",\n      \"configurePreset\": \"debug\",\n      \"displayName\": \"Test Debug\",\n      \"output\": {\n        \"outputOnFailure\": true\n      }\n    },\n    {\n      \"name\": \"release\",\n      \"configurePreset\": \"release\",\n      \"displayName\": \"Test Release\",\n      \"output\": {\n        \"outputOnFailure\": true\n      }\n    },\n    {\n      \"name\": \"unixlike-release\",\n      \"configurePreset\": \"unixlike-release\",\n      \"displayName\": \"Test Unix Like Compiler Release\",\n      \"output\": {\n        \"outputOnFailure\": true\n      }\n    },\n    {\n      \"name\": \"unixlike-debug\",\n      \"configurePreset\": \"unixlike-debug\",\n      \"displayName\": \"Test Unix Like Compiler Debug\",\n      \"output\": {\n        \"outputOnFailure\": true\n      }\n    },\n    {\n      \"name\": \"unixlike-debug-dev\",\n      \"configurePreset\": \"unixlike-debug-dev\",\n      \"displayName\": \"Test Unix Like Compiler Debug (Dev)\",\n      \"output\": {\n        \"outputOnFailure\": true\n      }\n    },\n    {\n      \"name\": \"unixlike-release-dev\",\n      \"configurePreset\": \"unixlike-release-dev\",\n      \"displayName\": \"Test Unix Like Compiler Release (Dev)\",\n      \"output\": {\n        \"outputOnFailure\": true\n      }\n    },\n    {\n      \"name\": \"msvc-debug-dev\",\n      \"configurePreset\": \"msvc-debug-dev\",\n      \"displayName\": \"Test MSVC Debug (Dev)\",\n      \"output\": {\n        \"outputOnFailure\": true\n      }\n    },\n    {\n      \"name\": \"msvc-release-dev\",\n      \"configurePreset\": \"msvc-release-dev\",\n      \"displayName\": \"Test MSVC Release (Dev)\",\n      \"output\": {\n        \"outputOnFailure\": true\n      }\n    },\n    {\n      \"name\": \"clangcl-debug-dev\",\n      \"configurePreset\": \"clangcl-debug-dev\",\n      \"displayName\": \"Test ClangCL Debug (Dev)\",\n      \"output\": {\n        \"outputOnFailure\": true\n      }\n    },\n    {\n      \"name\": \"clangcl-release-dev\",\n      \"configurePreset\": \"clangcl-release-dev\",\n      \"displayName\": \"Test ClangCL Release (Dev)\",\n      \"output\": {\n        \"outputOnFailure\": true\n      }\n    }\n  ]\n}"
  },
  {
    "path": "LICENSE_1_0.txt",
    "content": "Boost Software License - Version 1.0 - August 17th, 2003\n\nPermission is hereby granted, free of charge, to any person or organization\nobtaining a copy of the software and accompanying documentation covered by\nthis license (the \"Software\") to use, reproduce, display, distribute,\nexecute, and transmit the Software, and to prepare derivative works of the\nSoftware, and to permit third-parties to whom the Software is furnished to\ndo so, all subject to the following:\n\nThe copyright notices in the Software and this entire statement, including\nthe above license grant, this restriction and the following disclaimer,\nmust be included in all copies of the Software, in whole or in part, and\nall derivative works of the Software, unless such copies or derivative\nworks are solely in the form of machine-executable object code generated by\na source language processor.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\nSHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\nFOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "# The Art of C++\n# https://github.com/taocpp\n\n# Copyright (c) 2016-2026 Daniel Frey\n# Distributed under the Boost Software License, Version 1.0.\n# (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n.SUFFIXES:\n.SECONDARY:\n\nifeq ($(OS),Windows_NT)\nUNAME_S := $(OS)\nifeq ($(shell gcc -dumpmachine),mingw32)\nMINGW_CXXFLAGS = -U__STRICT_ANSI__\nendif\nelse\nUNAME_S := $(shell uname -s)\nendif\n\n# For Darwin (Mac OS X / macOS) we assume that the default compiler\n# clang++ is used; when $(CXX) is some version of g++, then\n# $(CXXSTD) has to be set to -std=c++20 (or newer) so\n# that -stdlib=libc++ is not automatically added.\n\nifeq ($(CXXSTD),)\nCXXSTD := -std=c++20\nifeq ($(UNAME_S),Darwin)\nCXXSTD += -stdlib=libc++\nendif\nendif\n\n# Ensure strict standard compliance and no warnings, can be\n# changed if desired.\n\nBUILDDIR ?= build\n\nINCFLAGS ?= -Iinclude -Itest $(patsubst %,-I%,$(shell pg_config --includedir))\nCPPFLAGS ?= -pedantic\nCXXFLAGS ?= -Wall -Wextra -Wshadow -Werror -O3 $(MINGW_CXXFLAGS)\nLDFLAGS ?= -rdynamic $(patsubst %,-L%,$(shell pg_config --libdir))\nLIBS ?= -lpq\n\nCLANG_TIDY ?= clang-tidy\n\nHEADERS := $(shell find include -name '*.hpp')\nSOURCES := $(shell find src -name '*.cpp')\nTESTSOURCES := $(shell find test -name '*.cpp')\nDEPENDS := $(SOURCES:%.cpp=$(BUILDDIR)/%.d) $(TESTSOURCES:%.cpp=$(BUILDDIR)/%.d)\nBINARIES := $(SOURCES:%.cpp=$(BUILDDIR)/%) $(TESTSOURCES:%.cpp=$(BUILDDIR)/%)\n\nUNIT_TESTS := $(filter $(BUILDDIR)/test/%,$(BINARIES))\n\nLIBSOURCES := $(filter src/lib/%,$(SOURCES))\nLIBNAME := taopq\n\n.PHONY: all\nall: check\n\n.PHONY: compile\ncompile: $(UNIT_TESTS)\n\n.PHONY: check\ncheck: $(UNIT_TESTS)\n\t@set -e; for T in $(UNIT_TESTS); do echo $$T; $$T; done\n\n$(BUILDDIR)/%.clang-tidy: % .clang-tidy\n\t$(CLANG_TIDY) -quiet $< -- $(CXXSTD) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) 2>/dev/null\n\t@mkdir -p $(@D)\n\t@touch $@\n\n.PHONY: clang-tidy\nclang-tidy: $(HEADERS:%=$(BUILDDIR)/%.clang-tidy) $(SOURCES:%=$(BUILDDIR)/%.clang-tidy)\n\t@echo \"All $(words $(HEADERS) $(SOURCES)) clang-tidy tests passed.\"\n\n.PHONY: clean\nclean:\n\t@rm -rf $(BUILDDIR)/*\n\t@find . -name '*~' -delete\n\n$(BUILDDIR)/%.d: %.cpp Makefile\n\t@mkdir -p $(@D)\n\t$(CXX) $(CXXSTD) $(INCFLAGS) $(CPPFLAGS) -MM -MQ $@ $< -o $@\n\n$(BUILDDIR)/%.o: %.cpp $(BUILDDIR)/%.d\n\t$(CXX) $(CXXSTD) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@\n\n$(BUILDDIR)/lib/lib$(LIBNAME).a: $(LIBSOURCES:%.cpp=$(BUILDDIR)/%.o)\n\t@mkdir -p $(@D)\n\t$(AR) -rcs $@ $^\n\n.PHONY: lib\nlib: $(BUILDDIR)/lib/lib$(LIBNAME).a\n\n$(BUILDDIR)/%: $(BUILDDIR)/%.o $(BUILDDIR)/lib/lib$(LIBNAME).a\n\t@mkdir -p $(@D)\n\t$(CXX) $(CXXSTD) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $^ $(LIBS) -o $@\n\nifeq ($(findstring $(MAKECMDGOALS),clean),)\n-include $(DEPENDS)\nendif\n"
  },
  {
    "path": "README.md",
    "content": "# Welcome to taoPQ\n\n[![Windows CI](https://github.com/taocpp/taopq/workflows/Windows/badge.svg)](https://github.com/taocpp/taopq/actions?query=workflow%3AWindows)\n[![macOS CI](https://github.com/taocpp/taopq/workflows/macOS/badge.svg)](https://github.com/taocpp/taopq/actions?query=workflow%3AmacOS)\n[![Linux CI](https://github.com/taocpp/taopq/workflows/Linux/badge.svg)](https://github.com/taocpp/taopq/actions?query=workflow%3ALinux)\n<br>\n[![clang-analyze](https://github.com/taocpp/taopq/workflows/clang-analyze/badge.svg)](https://github.com/taocpp/taopq/actions?query=workflow%3Aclang-analyze)\n[![clang-tidy](https://github.com/taocpp/taopq/workflows/clang-tidy/badge.svg)](https://github.com/taocpp/taopq/actions?query=workflow%3Aclang-tidy)\n[![Sanitizer](https://github.com/taocpp/taopq/workflows/Sanitizer/badge.svg)](https://github.com/taocpp/taopq/actions?query=workflow%3ASanitizer)\n[![Codecov](https://codecov.io/gh/taocpp/taopq/graph/badge.svg?token=jrFgbSi3wY)](https://codecov.io/gh/taocpp/taopq)\n\ntaoPQ is a lightweight C++ client library for accessing a [PostgreSQL➚](https://www.postgresql.org/) database.\nIt has no dependencies beyond [`libpq`➚](https://www.postgresql.org/docs/current/libpq.html), the C application programmer's interface to PostgreSQL.\n\n## Introduction\n\nThe library provides support for database connections, transactions, nested transactions, prepared statements, large objects, connection pools, pipeline mode, high-speed bulk data transfer, and more.\nAn extensible traits mechanism is used to convert C++ types into SQL statement parameters, and conversely to convert query results into arbitrary C++ types.\nThe following example shows the basic look and feel of the library.\n\n```c++\n#include <iostream>\n#include <tao/pq.hpp>\n\nint main()\n{\n   // open a connection to the database\n   const auto conn = tao::pq::connection::create( \"dbname=template1\" );\n\n   // execute statements\n   conn->execute( \"DROP TABLE IF EXISTS users\" );\n   conn->execute( \"CREATE TABLE users ( name TEXT PRIMARY KEY, age INTEGER NOT NULL )\" );\n\n   // prepare statements\n   conn->prepare( \"insert_user\", \"INSERT INTO users ( name, age ) VALUES ( $1, $2 )\" );\n\n   {\n      // begin transaction\n      const auto tr = conn->transaction();\n\n      // execute previously prepared statements\n      tr->execute( \"insert_user\", \"Daniel\", 42 );\n      tr->execute( \"insert_user\", \"Tom\", 41 );\n      tr->execute( \"insert_user\", \"Jerry\", 29 );\n\n      // commit transaction\n      tr->commit();\n   }\n\n   // query data\n   const auto users = conn->execute( \"SELECT name, age FROM users WHERE age >= $1\", 40 );\n\n   // iterate and convert results\n   for( const auto& row : users ) {\n      std::cout << row[ \"name\" ].as< std::string >() << \" is \"\n                << row[ \"age\" ].as< unsigned >() << \" years old.\\n\";\n   }\n}\n```\n\n## Documentation\n\n* [Table of Content](doc/TOC.md)\n* [Requirements](doc/Requirements.md)\n* [Installation](doc/Installation.md)\n* [Getting Started](doc/Getting-Started.md)\n* [Connection](doc/Connection.md)\n* [Transaction](doc/Transaction.md)\n* [Statement](doc/Statement.md)\n* [Result](doc/Result.md)\n\n## Contact\n\nFor questions and suggestions regarding taoPQ, success or failure stories, and any other kind of feedback, please feel free to open a [discussion](https://github.com/taocpp/taopq/discussions), an [issue](https://github.com/taocpp/taopq/issues) or a [pull request](https://github.com/taocpp/taopq/pulls) on GitHub or contact the authors at `taocpp(at)icemx.net`.\n\n## The Art of C++\n\ntaoPQ is part of [The Art of C++](https://taocpp.github.io/).\n\n[<img alt=\"colinh\" src=\"https://avatars.githubusercontent.com/u/113184\" width=\"120\">](https://github.com/colinh)\n[<img alt=\"d-frey\" src=\"https://avatars.githubusercontent.com/u/3956325\" width=\"120\">](https://github.com/d-frey)\n[<img alt=\"uilianries\" src=\"https://avatars.githubusercontent.com/u/4870173\" width=\"120\">](https://github.com/uilianries)\n\n## License\n\n<a href=\"https://opensource.org/licenses/BSL-1.0\"><img align=\"right\" src=\"https://opensource.org/wp-content/uploads/2009/06/OSIApproved.svg\" width=\"150\" hspace=\"20\" alt=\"Open Source Initiative\"></a>\n\nCopyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n\ntaoPQ is certified [Open Source➚](http://www.opensource.org/docs/definition.html) software.\nIt is [licensed➚](https://pdimov.github.io/blog/2020/09/06/why-use-the-boost-license/) under the terms of the [Boost Software License, Version 1.0➚](https://www.boost.org/LICENSE_1_0.txt) reproduced here.\n\n> Boost Software License - Version 1.0 - August 17th, 2003\n>\n> Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the \"Software\") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following:\n>\n> The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor.\n>\n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n[The Art of C++]: https://taocpp.github.io/\n"
  },
  {
    "path": "doc/Aggregate-Support.md",
    "content": "# Aggregate Support\n\ntaoPQ allows the direct use of \"simple\" aggregates as parameters and result types.\n\n## Requirements\n\nAn aggregate data type `T` suitable for taoPQ support must meet the following requirements:\n\n* [`std::is_aggregate_v< T >`➚](https://en.cppreference.com/w/cpp/types/is_aggregate) must yield `true`.\n* [`std::is_empty_v< T >`➚](https://en.cppreference.com/w/cpp/types/is_empty) must yield `false`.\n* [`std::is_union_v< T >`➚](https://en.cppreference.com/w/cpp/types/is_union) must yield `false`.\n* `T` must not have any base classes.\n* `T` must not have more than 99 member variables. (this limit can be raised, open an [issue](https://github.com/taocpp/taopq/issues) if necessary)\n\n## Registration\n\nWe currently require explicit registration of aggregate data types.\nThis is done as a precaution, and to avoid conflicts with other data types.\n\nA data type `T` can be registered as both a parameter and a result type by specializing `tao::pq::is_aggregate` as follows:\n\n```c++\ntemplate<>\ninline constexpr bool tao::pq::is_aggregate< T > = true;\n```\n\nIf necessary, you can control independently whether a data type is suitable as a parameter or a result type by specializing `tao::pq::is_aggregate_parameter` and `tao::pq::is_aggregate_result` as follows:\n\n```c++\ntemplate<>\ninline constexpr bool tao::pq::is_aggregate_parameter< T > = true;\n\ntemplate<>\ninline constexpr bool tao::pq::is_aggregate_result< T > = true;\n```\n\n## Direct Result Conversion\n\nAn aggregate data type `T` that is registered as a result type enables the direct conversion of a `tao::pq::row` to `T`.\nThis is convenient when retrieving results and iterating over them, it enables you to write:\n\n```c++\nconst auto result = conn->execute( \"SELECT ... FROM ...\" ); // some query\nfor( const T t : result ) {\n   // use t\n}\n```\n\n:interrobang: Compilers might complain about extra copies and that you should use `const T&`, but that would not work and there are no extra copies.\n\n## Example\n\nThe following is a short, but complete example of how to use aggregates with taoPQ:\n\n```c++\n#include <iostream>\n#include <tao/pq.hpp>\n\n// an aggregate\nstruct user\n{\n   std::string name;\n   unsigned age;\n   std::string planet;\n};\n\ntemplate<>\ninline constexpr bool tao::pq::is_aggregate< user > = true;\n\nint main()\n{\n   // open a connection to the database\n   const auto conn = tao::pq::connection::create( \"dbname=template1\" );\n\n   // execute statements\n   conn->execute( \"DROP TABLE IF EXISTS users\" );\n   conn->execute( \"CREATE TABLE users ( name TEXT PRIMARY KEY, age INTEGER NOT NULL, planet TEXT NOT NULL )\" );\n\n   // prepare statements\n   conn->prepare( \"insert_user\", \"INSERT INTO users ( name, age, planet ) VALUES ( $1, $2, $3 )\" );\n\n   // execute previously prepared statements\n   conn->execute( \"insert_user\", user{ \"R. Daneel Olivaw\", 19230, \"Earth\" } );\n   conn->execute( \"insert_user\", user{ \"R. Giskard Reventlov\", 42, \"Aurora\" } );\n\n   // query and convert data\n   for( const user u : conn->execute( \"SELECT name, age, planet FROM users\" ) ) {\n      std::cout << u.name << \" from \" << u.planet << \" was \" << u.age << \" years old.\\n\";\n   }\n}\n```\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Binary-Data.md",
    "content": "# Binary Data\n\nPostgreSQL stores binary data either as a field with the [`BYTEA`➚](https://www.postgresql.org/docs/current/datatype-binary.html) data type or as a [large object➚](https://www.postgresql.org/docs/current/largeobjects.html).\n\nLarge Objects in taoPQ have their own representation discussed in the [Large Object](Large-Object.md) chapter.\n\n## The [`BYTEA`➚](https://www.postgresql.org/docs/current/datatype-binary.html) Data Type\n\nIn PostgreSQL strings are a sequence of bytes that is valid in a given encoding.\nOne or more bytes can represent individual characters or code points.\nThis means that not all sequences of bytes are a valid string and therefore binary data can generally not be represented by PostgreSQL as a string.\n\nBinary data is different from strings, as binary data is a collection of an arbitrary sequence of bytes.\nAny byte is treated independently of its surrounding bytes and can have any value, including '\\0'.\nIt is therefore crucial to represent binary data with the dedicated [`BYTEA`➚](https://www.postgresql.org/docs/current/datatype-binary.html) data type.\n\n## C++ Binary Data\n\nThe individual bytes are represented by [`std::byte`➚](https://en.cppreference.com/w/cpp/types/byte).\nAs there is no one-size-fits-all data type to handle binary data in C++, we allow several options.\n\nWe mostly represent binary data with `tao::pq::binary` and `tao::pq::binary_view`.\nThese are type aliases for [`std::vector<std::byte>`➚](https://en.cppreference.com/w/cpp/container/vector) and [`std::span<std::byte>`➚](https://en.cppreference.com/w/cpp/container/span), respectively.\n\n## Passing Binary Data\n\nWhen you pass binary data to taoPQ, we only require a view to be passed.\nAs a view is a non-owning data type, constructing an instance of it is cheap.\n\nIf you have other data types, you can create a binary data view by using\n\n```c++\nauto tao::pq::to_binary_view( const auto* data, const std::size_t size ) noexcept -> tao::pq::binary_view;\n\nauto tao::pq::to_binary_view( const auto& data ) noexcept\n{\n   return pq::to_binary_view( std::data( data ), std::size( data ) );\n}\n```\n\nThe former function requires (and checks) that `T` has a size of 1 byte.\nIf you want to store larger `T`s as binary data you need to manually convert the pointer and size appropriately.\n\nThe second method requires the data type `T` to be a suitable candidate for [`std::data()`➚](https://en.cppreference.com/w/cpp/iterator/data) and [`std::size()`➚](https://en.cppreference.com/w/cpp/iterator/size), which requires the data to be stored in a contiguous block of memory.\nWe do not offer any convenience methods to create binary data from distributed data structures, i.e. `std::list<std::byte>` is not supported.\n\n## Receiving Binary Data\n\nWhen receiving binary data, a non-owning view is insufficient, hence we return `tao::pq::binary`.\nIn some cases other alternatives are offered, i.e. you may provide a buffer that the data is written to.\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Bulk-Transfer.md",
    "content": "# Bulk Transfer\n\n**TODO**\n\n## Synopsis\n\nDon't be intimidated by the size of the API, as you can see several methods are just single-line convenience forwarders.\n\nWe will first give the synopsis of everything, afterwards we will break down the API into small logical portions.\n\n```c++\nnamespace tao::pq\n{\n   namespace internal\n   {\n      class zsv;  // zero-terminated string view\n   }\n\n   class transaction;\n\n   class table_writer final\n   {\n   public:\n      template< typename... As >\n      table_writer( const std::shared_ptr< transaction >& transaction, const internal::zsv statement, As&&... as );\n\n      ~table_writer();\n\n      table_writer( const table_writer& ) = delete;\n      table_writer( table_writer&& ) = delete;\n      void operator=( const table_writer& ) = delete;\n      void operator=( table_writer&& ) = delete;\n\n      void insert_raw( const std::string_view data );\n\n      template< typename... As >\n      void insert( As&&... as );\n\n      auto commit() -> std::size_t;\n   };\n\n   using null_t = decltype( null );\n\n   class table_row;\n   class table_field;\n\n   class table_reader final\n   {\n   private:\n      class const_iterator;\n\n   public:\n      template< typename... As >\n      table_reader( const std::shared_ptr< transaction >& transaction, const internal::zsv statement, As&&... as );\n\n      ~table_reader() = default;\n\n      table_reader( const table_reader& ) = delete;\n      table_reader( table_reader&& ) = delete;\n      void operator=( const table_reader& ) = delete;\n      void operator=( table_reader&& ) = delete;\n\n      auto columns() const noexcept -> std::size_t;\n\n      auto get_raw_data() -> std::string_view;\n      bool parse_data() noexcept;\n\n      bool get_row();\n      bool has_data() const noexcept;\n\n      auto raw_data() const noexcept\n         -> const std::vector< const char* >&;\n\n      auto row() noexcept -> table_row;\n\n      auto begin() -> const_iterator;\n      auto end() noexcept -> const_iterator;\n\n      auto cbegin() -> const_iterator;\n      auto cend() noexcept -> const_iterator;\n\n      template< typename T >\n      auto as_container() -> T;\n\n      // convenience conversions to standard containers\n      template< typename... Ts >\n      auto vector()\n      {\n         return as_container< std::vector< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto list()\n      {\n         return as_container< std::list< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto set()\n      {\n         return as_container< std::set< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto multiset()\n      {\n         return as_container< std::multiset< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto unordered_set()\n      {\n         return as_container< std::unordered_set< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto unordered_multiset()\n      {\n         return as_container< std::unordered_multiset< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto map()\n      {\n         return as_container< std::map< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto multimap()\n      {\n         return as_container< std::multimap< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto unordered_map()\n      {\n         return as_container< std::unordered_map< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto unordered_multimap()\n      {\n         return as_container< std::unordered_multimap< Ts... > >();\n      }\n   };\n\n   class table_row\n   {\n   private:\n      // satisfies LegacyRandomAccessIterator, see\n      // https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator\n      class const_iterator;\n\n   public:\n      auto slice( const std::size_t offset, const std::size_t in_columns ) const -> table_row;\n\n      auto columns() const noexcept -> std::size_t;\n\n      // iteration\n      auto begin() const -> const_iterator;\n      auto end() const -> const_iterator;\n\n      auto cbegin() const -> const_iterator;\n      auto cend() const -> const_iterator;\n\n      bool is_null( const std::size_t column ) const;\n      auto get( const std::size_t column ) const -> const char*;\n\n      template< typename T >\n      auto get( const std::size_t column ) const -> T;\n\n      template< typename T >\n      auto optional( const std::size_t column ) const\n      {\n         return get< std::optional< T > >( column );\n      }\n\n      template< typename T >\n      auto as() const -> T;\n\n      template< typename T >\n      auto optional() const\n      {\n         return as< std::optional< T > >();\n      }\n\n      template< typename T, typename U >\n      auto pair() const\n      {\n         return as< std::pair< T, U > >();\n      }\n\n      template< typename... Ts >\n      auto tuple() const\n      {\n         return as< std::tuple< Ts... > >();\n      }\n\n      auto at( const std::size_t column ) const -> table_field;\n      auto operator[]( const std::size_t column ) const noexcept -> table_field;\n\n      friend void swap( table_row& lhs, table_row& rhs ) noexcept;\n   };\n\n   class table_field\n   {\n   public:\n      auto index() const -> std::size_t;\n\n      bool is_null() const;\n      auto get() const -> const char*;\n\n      template< typename T >\n      auto as() const -> T;\n\n      template< typename T >\n      auto optional() const\n      {\n         return as< std::optional< T > >();\n      }\n   };\n\n   bool operator==( const table_field& f, null_t )\n   {\n      return f.is_null();\n   }\n\n   bool operator==( null_t, const table_field& f )\n   {\n      return f.is_null();\n   }\n\n   bool operator!=( const table_field& f, null_t )\n   {\n      return !f.is_null();\n   }\n\n   bool operator!=( null_t, const table_field& f )\n   {\n      return !f.is_null();\n   }\n}\n```\n\n**TODO**\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Connection-Pool.md",
    "content": "# Connection Pool\n\nOpening a new connection to the database server typically consists of several time-consuming steps.\nA physical channel such as a socket or a named pipe must be established, the initial handshake with the server must occur, if encryption protocols (SSL) are enabled those need to be established, the connection string information must be parsed, the connection must be authenticated by the server, checks must be run for enlisting in the current transactions, and so on.\n\nIn practice, most applications use only one or a few different configurations for connections.\nThis means that during application execution, many identical connections will be repeatedly opened and closed.\nTo minimize the cost of opening connections, taoPQ provides connection pools.\n\nConnection pools reduces the number of times that new connections must be opened.\nThey manage connections by keeping alive a set of active connections and borrowing them to the application on demand.\nThe connection pool maintains ownership of the connections when they are not used by the application.\n\n## Synopsis\n\n```c++\nnamespace tao::pq\n{\n   namespace internal\n   {\n      class zsv;  // zero-terminated string view\n   }\n\n   namespace poll\n   {\n      enum class status\n      {\n         timeout,\n         readable,\n         writable,\n         again\n      };\n\n      using callback = status( const int socket,\n                               const bool wait_for_write,\n                               const int timeout_ms );\n   }\n\n   class connection;\n\n   class connection_pool final\n      : public std::enable_shared_from_this< connection_pool >\n   {\n   public:\n      // create a new connection pool\n      static auto create( const std::string& connection_info,\n                          std::function< tao::pq::poll::callback > poll_cb = /*unspecified*/ )\n         -> std::shared_ptr< connection_pool >;\n\n      // non-copyable, non-movable\n      connection_pool( const connection_pool& ) = delete;\n      connection_pool( connection_pool&& ) = delete;\n      void operator=( const connection_pool& ) = delete;\n      void operator=( connection_pool&& ) = delete;\n\n      virtual ~connection_pool() = default;\n\n      // timeout handling\n      auto timeout() const noexcept\n         -> const std::optional< std::chrono::milliseconds >&;\n\n      void set_timeout( const std::chrono::milliseconds timeout );\n      void reset_timeout() noexcept;\n\n      // customizable poll()-callback\n      auto poll_callback() const noexcept\n         -> const std::function< tao::pq::poll::callback >&;\n\n      void set_poll_callback( std::function< tao::pq::poll::callback > poll_cb ) noexcept;\n      void reset_poll_callback();\n\n      // borrow a connection\n      auto connection() const noexcept\n         -> std::shared_ptr< pq::connection >;\n\n      // direct statement execution\n      template< typename... As >\n      auto execute( const internal::zsv statement, As&&... as )\n      {\n         return connection()->execute( statement, std::forward< As >( as )... );\n      }\n\n      // checks whether the pool contains idle connections\n      auto empty() const noexcept\n        -> bool;\n\n      // number of idle connections\n      auto size() const noexcept\n        -> std::size_t;\n\n      // number of borrowed connections\n      auto attached() const noexcept\n        -> std::size_t;\n\n      // cleanup\n      void erase_invalid();\n   };\n}\n```\n\n:point_up: Note that `tao::pq::internal::zsv` is explained in the [Statement](Statement.md) chapter.\n\n## Creating Connection Pools\n\nA connection pool is created by calling `tao::pq::connection_pool`'s static `create()`-method.\n\n```c++\nauto tao::pq::connection_pool::create( const std::string& connection_info )\n    -> std::shared_ptr< tao::pq::connection_pool >;\n```\n\nIt takes a mandatory parameter, the [connection string➚](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING), that is used when new connections are opened by the pool.\n\nThe second, optional parameter can be used to specify the default `poll()`-callback for connections, see [Customizable `poll()`-callback](Connection.md#customizable-poll-callback).\n\n## Borrowing Connections\n\nWhen you need a connection, you simply call the `connection()`-method.\n\n```c++\nauto tao::pq::connection_pool::connection()\n    -> std::shared_ptr< tao::pq::connection >;\n```\n\nThis will either open a new connection when the pool is empty, or it will give you a reused connection from the pool.\nAs long as you retain ownership of the returned shared pointer, it is yours to work with.\nWhen the last remaining shared pointer is destroyed or assigned another value, the connection is returned to the pool.\n\n## Executing Statements\n\nYou can [execute statements](Statement.md) on a connection pool directly, which is equivalent to borrowing a temporary connection (as if calling the `connection()`-method) and executing the statement on that [connection](Connection.md).\nAfter the statement was executed, the temporary connection is returned to the pool.\n\n## Cleanup\n\nThe connection pool will implicitly discard connections that are in a failed state when they are returned to the pool or when they are retrieved from the pool.\n\nIn some environments you might need to periodically clean up the connection pool to get rid of connections that are no longer valid.\nIn order to do so, just call the `erase_invalid()`-method, which will check the status of each pooled connection and discard the invalid ones.\n\n```c++\nvoid tao::pq::connection_pool::erase_invalid();\n```\n\n## Customizable `poll()`-callback\n\nThe default implementation for polling uses `poll()` or `WSAPoll()`, depending on your system.\nThis callback can be customized to support other I/O frameworks, e.g. Boost.Asio.\n\nTo access the current default callback for borrowed connections you can call the `poll_callback()`-method.\n\n```c++\nauto poll_callback() const noexcept\n   -> const std::function< tao::pq::poll::callback >&;\n```\n\nSetting the default `poll()`-callback for borrowed connections is done by calling the `set_poll_callback()`-method.\n\n```c++\nvoid set_poll_callback( std::function< tao::pq::poll::callback > poll_cb ) noexcept;\n```\n\nYou can revert the current default `poll()`-callback for borrowed connections to the default by calling the `reset_poll_callback()`-method.\n\n```c++\nvoid reset_poll_callback();\n```\n\n## Thread Safety\n\nThe connection pool's borrowing mechanism is thread-safe, i.e. multiple threads can make calls to the `connection()`-method or return connections simultaneously.\nYou can also call the `erase_invalid()`-method at any time.\n\nInternally, the connection pool uses a [mutex➚](https://en.cppreference.com/w/cpp/thread/mutex) to serialize the above operations.\nWe minimized the work in the [critical sections➚](https://en.wikipedia.org/wiki/Critical_section) as far as possible.\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Connection.md",
    "content": "# Connection\n\nAll communication with a database server is handled through a connection, represented by the `tao::pq::connection` type in taoPQ.\nA connection object takes care of tracking [transactions](Transaction.md), [error handling](Error-Handling.md), and it has its own set of prepared statements.\n\n## Synopsis\n\n```c++\nnamespace tao::pq\n{\n   namespace internal\n   {\n      class zsv;  // zero-terminated string view\n   }\n\n   namespace poll\n   {\n      enum class status\n      {\n         timeout,\n         readable,\n         writable,\n         again\n      };\n\n      using callback = status( const int socket,\n                               const bool wait_for_write,\n                               const int timeout_ms );\n   }\n\n   enum class isolation_level\n   {\n      default_isolation_level,\n      serializable,\n      repeatable_read,\n      read_committed,\n      read_uncommitted\n   };\n\n   enum class access_mode\n   {\n      default_access_mode,\n      read_write,\n      read_only\n   };\n\n   class notification final\n   {\n   public:\n      auto channel() const noexcept -> const char*;\n      auto payload() const noexcept -> const char*;\n\n      auto underlying_raw_ptr() noexcept -> PGnotify*;\n      auto underlying_raw_ptr() const noexcept -> const PGnotify*;\n   };\n\n   class transaction;\n\n   class connection final\n      : public std::enable_shared_from_this< connection >\n   {\n   public:\n      // create a new connection\n      static auto create( const std::string& connection_info,\n                          std::function< tao::pq::poll::callback > poll_cb = /*unspecified*/ )\n         -> std::shared_ptr< connection >;\n\n      // non-copyable, non-movable\n      connection( const connection& ) = delete;\n      connection( connection&& ) = delete;\n      void operator=( const connection& ) = delete;\n      void operator=( connection&& ) = delete;\n\n      ~connection() = default;\n\n      // query status\n      bool is_open() const noexcept;\n      bool is_idle() const noexcept;\n\n      // create transactions\n      auto direct()\n         -> std::shared_ptr< pq::transaction >;\n\n      auto transaction()\n         -> std::shared_ptr< pq::transaction >;\n\n      auto transaction( const access_mode am,\n                        const isolation_level il = isolation_level::default_isolation_level )\n         -> std::shared_ptr< pq::transaction >;\n\n      auto transaction( const isolation_level il,\n                        const access_mode am = access_mode::default_access_mode )\n         -> std::shared_ptr< pq::transaction >;\n\n      // timeout handling\n      auto timeout() const noexcept\n         -> const std::optional< std::chrono::milliseconds >&;\n\n      void set_timeout( const std::chrono::milliseconds timeout );\n      void reset_timeout() noexcept;\n\n      // prepared statements\n      void prepare( const std::string& name, const std::string& statement );\n      void deallocate( const std::string& name );\n\n      // direct statement execution\n      template< typename... As >\n      auto execute( const internal::zsv statement, As&&... as )\n      {\n         return direct()->execute( statement, std::forward< As >( as )... );\n      }\n\n      // listen/notify support\n      void listen( const std::string_view channel );\n      void listen( const std::string_view channel, const std::function< void( const char* ) >& handler );\n\n      void unlisten( const std::string_view channel );\n\n      void notify( const std::string_view channel );\n      void notify( const std::string_view channel, const std::string_view payload );\n\n      auto notification_handler()\n         -> std::function< void( const notification& ) >;\n\n      void set_notification_handler( const std::function< void( const notification& ) >& handler );\n      void reset_notification_handler() noexcept;\n\n      auto notification_handler( const std::string_view channel )\n         -> std::function< void( const char* ) >;\n\n      void set_notification_handler( const std::string_view channel, const std::function< void( const char* ) >& handler );\n      void reset_notification_handler( const std::string_view channel ) noexcept;\n\n      void handle_notifications();\n      void get_notifications();\n\n      // customizable poll()-callback\n      auto poll_callback() const noexcept\n         -> const std::function< tao::pq::poll::callback >&;\n\n      void set_poll_callback( std::function< tao::pq::poll::callback > poll_cb ) noexcept;\n      void reset_poll_callback();\n\n      // access underlying connection pointer from libpq\n      auto underlying_raw_ptr() noexcept -> PGconn*;\n      auto underlying_raw_ptr() const noexcept -> const PGconn*;\n\n      // access the socket used by libpq\n      auto socket() const -> int;\n\n      // error message\n      auto error_message() const -> std::string;\n   };\n}\n```\n\n:point_up: Note that `tao::pq::internal::zsv` is explained in the [Statement](Statement.md) chapter.\n\n## Creating a Connection\n\nA connection is created by calling `tao::pq::connection`'s static `create()`-method.\n\n```c++\nauto tao::pq::connection::create( const std::string& connection_info,\n                                  std::function< tao::pq::poll::callback > poll_cb = /*unspecified*/ )\n    -> std::shared_ptr< tao::pq::connection >;\n```\n\nIt takes a mandatory parameter, the [connection string➚](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING), that is passed to the underlying `libpq` for opening the database connection.\nThe connection string contains parameters and options, such as the server address or the database name.\nConnection parameters that are not specified in the connection string might also be set via [environment variables➚](https://www.postgresql.org/docs/current/libpq-envars.html).\n\nThe second, optional parameter can be used to specify a `poll()`-callback, see [Customizable `poll()`-callback](#customizable-poll-callback).\n\nThe method returns a `std::shared_ptr<tao::pq::connection>` or, in case of an error, throws an exception.\nWhen the last reference to a connection is deleted, i.e. the last shared pointer referencing it is deleted or reset, the connection is closed via its destructor which takes care of freeing underlying resources.\nThe shared pointer might also be stored internally in other objects of taoPQ, i.e. a transaction.\nThis ensures, that the connection is kept alive as long as there are dependent objects like an active transaction, see below.\n\n## Creating Transactions\n\nYou can create [transactions](Transaction.md) by calling either the `direct()`-method or the `transaction()`-method.\nBoth methods register the newly created transaction as the active transaction of a connection.\nA connection can only have one active transaction at any given time.\nFurther details on how to use transactions are discussed in the [Transaction](Transaction.md) chapter.\n\n### Creating a \"Direct\" Transaction\n\nThe `direct()`-method creates an auto-commit transaction proxy, i.e. all statements executed on this transaction are immediately committed to the database.\n\n```c++\nauto tao::pq::connection::direct()\n    -> std::shared_ptr< tao::pq::transaction >;\n```\n\n:point_up: This is not a real transaction from the database's point of view, therefore calling the `commit()`- or `rollback()`-method on the transaction has no immediate effect on the database.\nHowever, calling either the `commit()`- or `rollback()`-method will end the transaction's logical lifetime and it will unregister itself from the connection.\n\n### Creating a Database Transaction\n\nThe `transaction()`-method begins a real [database transaction➚](https://www.postgresql.org/docs/current/tutorial-transactions.html).\n\n```c++\nauto tao::pq::connection::transaction()\n    -> std::shared_ptr< tao::pq::transaction >;\n\nauto tao::pq::connection::transaction( const tao::pq::isolation_level il,\n                                       const tao::pq::access_mode am = tao::pq::access_mode::default_access_mode )\n    -> std::shared_ptr< tao::pq::transaction >;\n\nauto tao::pq::connection::transaction( const tao::pq::access_mode am,\n                                       const tao::pq::isolation_level il = tao::pq::isolation_level::default_isolation_level )\n    -> std::shared_ptr< tao::pq::transaction >;\n```\n\nYou may specify two optional parameters, the [isolation level➚](https://www.postgresql.org/docs/current/transaction-iso.html) and the [access mode➚](https://www.postgresql.org/docs/current/sql-set-transaction.html).\n\nWhen `tao::pq::isolation_level::default_isolation_level` or `tao::pq::access_mode::default_access_mode` are used the transaction inherits its isolation level or access mode from the session, as described in the [PostgreSQL documentation➚](https://www.postgresql.org/docs/current/sql-set-transaction.html).\n\n## Executing Statements\n\nYou can [execute statements](Statement.md) on a connection object directly, which is equivalent to creating a temporary direct transaction (as if calling the `direct()`-method) and executing the statement on that [transaction](Transaction.md).\n\n## Prepared Statements\n\nPrepared statements only last for the duration of a connection, and are bound to a connection, i.e. the set of prepared statements is independent for each connection.\n\nYou can [prepare➚](https://www.postgresql.org/docs/current/sql-prepare.html) a statement by calling the `prepare()`-method.\n\n```c++\nvoid tao::pq::connection::prepare( const std::string& name, const std::string& statement );\n```\n\nIt takes two parameters, the name of the prepared statement and the SQL statement itself.\ntaoPQ limits the name to classic C-style identifiers, i.e. a non-empty sequence of digits, underscores, and lowercase and uppercase Latin letters.\nA valid identifier must begin with a non-digit character.\nIdentifiers are case-sensitive (lowercase and uppercase letters are distinct).\n\nA previously prepared statement can be [deallocated➚](https://www.postgresql.org/docs/current/sql-deallocate.html), although this is rare in pratice.\nTo deallocate a prepared statement, call the `deallocate()`-method.\n\n```c++\nvoid tao::pq::connection::deallocate( const std::string& name );\n```\n\nUsing the `prepare()`- and `deallocate()`-methods makes taoPQ's connection object aware of the names of the prepared statements.\nThis allows the [execution](Statement.md) of those prepared statements transparently via an `execute()`-method.\n\n### Manually Prepared Statements\n\nYou can manually prepare statements by executing [`PREPARE`➚](https://www.postgresql.org/docs/current/sql-prepare.html) statements directly via an `execute()`-method.\nWhile those prepared statements live on the same connection, there are some important differences.\nYou can only execute those prepared statements by executing [`EXECUTE`➚](https://www.postgresql.org/docs/current/sql-execute.html) statements directly via an `execute()`-method, and you can only deallocate them by executing [`DEALLOCATE`➚](https://www.postgresql.org/docs/current/sql-deallocate.html) statements directly via an `execute()`-method.\n\n:point_up: We advise to use the methods offered by taoPQ's connection type.\n\n## Checking Status\n\nYou can check a connection's status by calling the `is_open()`- or `is_idle()`-methods.\n\n```c++\nbool tao::pq::connection::is_open() const noexcept;\nbool tao::pq::connection::is_idle() const noexcept;\n```\n\nThe first method returns `true` when the connection is still open and usable, and `false` otherwise, i.e. if the connection is in a failed state.\nFor further details, check the documentation for the underlying [`PQstatus()`➚](https://www.postgresql.org/docs/current/libpq-status.html)-function provided by `libpq`.\n\nThe second method returns `true` when the connection is open and is in the idle state, and `false` otherwise.\nFor further details, check the documentation for the underlying [`PQtransactionStatus()`➚](https://www.postgresql.org/docs/current/libpq-status.html)-function provided by `libpq`.\n\n## Notification Framework\n\nPostgreSQL provides a simple [interprocess communication mechanism➚](https://www.postgresql.org/docs/current/sql-notify.html) for a collection of applications accessing the same database.\n\n### Sending Messages\n\nYou can send events with the `notify()`-method, providing a channel name and optionally a payload as the second parameter.\n\n```c++\nvoid tao::pq::connection::notify( const std::string_view channel );\nvoid tao::pq::connection::notify( const std::string_view channel, const std::string_view payload );\n```\n\n:point_up: The channel name is case sensitive when using taoPQ's methods.\n\n### Receiving Messages\n\nYou can subscribe to channels to receive messages using the `listen()`-method, or unsubscribe by calling the `unlisten()`-method.\n\n```c++\nvoid tao::pq::connection::listen( const std::string_view channel );\nvoid tao::pq::connection::unlisten( const std::string_view channel );\n```\n\nNote that subscriptions are per connection.\n\n### Handling Messages\n\nProcessing received messages requires you to register a notification handler.\nEach connection has its own notification handler.\nThe notification handler is managed by a `std::function< void( const tao::pq::notification& >` object.\n\nThe currently active notification handler is returned by the `notification_handler()`-method.\n\n```c++\nauto tao::pq::connection::notification_handler()\n   -> std::function< void( const tao::pq::notification& ) >;\n```\n\nIf no notification handler is set, the [`std::function`➚](https://en.cppreference.com/w/cpp/utility/functional/function) will be empty.\n\nSetting a notification handler is done by calling the `set_notification_handler()`-method.\n\n```c++\nvoid tao::pq::connection::set_notification_handler( const std::function< void( const tao::pq::notification& ) >& handler );\n```\n\nIf you want to deregister the current notification handler, you can call the `reset_notification_handler()`-method.\n\n```c++\nvoid tao::pq::connection::reset_notification_handler() noexcept;\n```\n\n### Per Channel Handlers\n\nBesides the above general notification handler, there is also the option to register a per channel handler.\nPer channel handlers only receive the payload as a parameter.\n\n```c++\nauto tao::pq::connection::notification_handler( const std::string_view channel )\n   -> std::function< void( const char* ) >;\n\nvoid tao::pq::connection::set_notification_handler( const std::string_view channel,\n                                                    const std::function< void( const char* ) >& handler );\nvoid tao::pq::connection::reset_notification_handler( const std::string_view channel ) noexcept;\n```\n\nWhen you subscribe to a channel with the `listen()`-method, you can optionally register a channel handler.\n\n```c++\nvoid tao::pq::connection::listen( const std::string_view channel,\n                                  const std::function< void( const char* ) >& handler );\n```\n\nThis registers the handler first by calling `set_notification_handler( channel, handler )`, then calls `listen( channel )`.\n\n### Asynchronous Notifications\n\ntaoPQ calls the registered notification handler(s) after successful execution by calling the `handle_notifications()`-method.\nAs a user, you rarely need to call the `handle_notifications()`-method manually.\n\n```c++\nvoid tao::pq::connection::handle_notifications();\n```\n\nWhen you don't have any statement to execute, you can call the `get_notifications()`-method which will actively query the server for new events.\n\n```c++\nvoid tao::pq::connection::get_notifications();\n```\n\n### Event Loop\n\n**TODO** Support event loops? How?\n\n## Customizable `poll()`-callback\n\nThe default implementation for polling uses `poll()` or `WSAPoll()`, depending on your system.\nThis callback can be customized to support other I/O frameworks, e.g. Boost.Asio.\n\nTo access the currently active callback you can call the `poll_callback()`-method.\n\n```c++\nauto poll_callback() const noexcept\n   -> const std::function< tao::pq::poll::callback >&;\n```\n\nSetting the `poll()`-callback is done by calling the `set_poll_callback()`-method.\n\n```c++\nvoid set_poll_callback( std::function< tao::pq::poll::callback > poll_cb ) noexcept;\n```\n\nYou can revert the current `poll()`-callback to the default by calling the `reset_poll_callback()`-method.\n\n```c++\nvoid reset_poll_callback();\n```\n\n## Underlying Connection Pointer\n\nIf you need to access the underlying raw connection pointer from `libpq`, you can call the `underlying_raw_ptr()`-method.\n\n```c++\nauto tao::pq::connection::underlying_raw_ptr() noexcept -> PGconn*;\nauto tao::pq::connection::underlying_raw_ptr() const noexcept -> const PGconn*;\n```\n\n## Error Messages\n\nYou can retrieve the last error message (if applicable) by calling the `error_message()`-method.\n\n```c++\nauto tao::pq::connection::error_message() const -> std::string;\n```\n\nWhen taoPQ throws an exception this is usually done internally and the message is part of the exception's `what()` message.\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Error-Handling.md",
    "content": "# Error Handling\n\n## SQL errors\n\nWhen an SQL statement is [executed](Statement.md) and the execution fails, an exception is thrown.\nThe base class for the exceptions thrown in this case is `tao::pq::sql_error`, which is derived from `tao::pq::error`, which in turn is derived from [`std::runtime_error`➚](https://en.cppreference.com/w/cpp/error/runtime_error).\n\n```c++\nnamespace tao::pq\n{\n   struct error\n      : std::runtime_error\n   {\n      using std::runtime_error::runtime_error;\n   };\n\n   struct sql_error\n      : error\n   {\n      std::string sqlstate;\n\n      // ctor...\n   };\n}\n```\n\nThe exception's `what()`-method will return the error message returned from the server.\n\n## SQLSTATE\n\nDepending on the [SQLSTATE➚](https://en.wikipedia.org/wiki/SQLSTATE) returned from the server as documented in [Appendix A➚](https://www.postgresql.org/docs/current/errcodes-appendix.html) of the PostgreSQL documentation, we throw an accordingly named exception class.\n\n### Class of Error\n\nThe first two characters of the error code denote the class of errors, while the last three characters indicate a specific condition within that class.\nThus, an application that does not recognize the specific error code might still be able to infer what to do from the error class.\n\nFor each class of errors, there is an exception class derived from `tao::pq::sql_error`.\nAs an example, if the class is \"02\" (\"no data\"), the exception class that is thrown is either derived from `tao::pq::no_data` if a more specific error condition is recognized, or `tao::pq::no_data` itself will be the exception class that is thrown if only the class itself is recognized by taoPQ.\n\n### Specific Error Conditions\n\nIf a specific error condition is recognized, an exception named after [Appendix A➚](https://www.postgresql.org/docs/current/errcodes-appendix.html) will be thrown, derived from the exception class of the class of error.\n\nThere are some cases in which the name can not be simply taken from that table, as they are used multiple times in different classes of errors.\nIn those cases the exception class is a class template and you need to add the class of error as a template parameter.\n\nSpecifically, this is necessary to distinguish the following exceptions:\n\n* `tao::pq::string_data_right_truncation< tao::pq::warning >` (SQLSTATE \"01004\")\n* `tao::pq::string_data_right_truncation< tao::pq::data_exception >` (SQLSTATE \"22001\")\n\nand these exceptions from class \"sql routine exception\":\n\n* `tao::pq::modifying_sql_data_not_permitted< tao::pq::sql_routine_exception >` (SQLSTATE \"2F002\")\n* `tao::pq::prohibited_sql_statement_attempted< tao::pq::sql_routine_exception >` (SQLSTATE \"2F003\")\n* `tao::pq::reading_sql_data_not_permitted< tao::pq::sql_routine_exception >` (SQLSTATE \"2F004\")\n\nvs these exceptions from class \"external routine exception\":\n\n* `tao::pq::modifying_sql_data_not_permitted< tao::pq::external_routine_exception >` (SQLSTATE \"38002\")\n* `tao::pq::prohibited_sql_statement_attempted< tao::pq::external_routine_exception >` (SQLSTATE \"38003\")\n* `tao::pq::reading_sql_data_not_permitted< tao::pq::external_routine_exception >` (SQLSTATE \"38004\")\n\n## Connection Errors\n\nPostgreSQL only delivers an SQLSTATE when a statement is executed.\nIn other situations, e.g. when opening a connection fails, no SQLSTATE is available.\nWe throw an exception of type `tao::pq::connection_error` in that case, with a dummy SQLSTATE of \"08000\".\n\nThe same exception can also be thrown when calling the connection's `get_notifications()`-method when the connection is broken.\n\n## Other Exceptions\n\n**TODO**\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Getting-Started.md",
    "content": "# Getting Started\n\nBefore we start with taoPQ, we'd like to point you to the excellent [PostgreSQL documentation➚](https://www.postgresql.org/docs/current/index.html).\nWe will assume that you are familiar with PostgreSQL and SQL in general, so we will *not* explain what a certain SQL statement does in the database.\n\nGetting started with taoPQ is really simple, a minimalistic program looks as follows:\n\n```c++\n#include <iostream>\n#include <string>\n\n#include <tao/pq.hpp>\n\nint main()\n{\n   const auto connection = tao::pq::connection::create( \"dbname=template1\" );\n   const auto result = connection->execute( \"SELECT version()\" );\n   std::cout << result.as< std::string >() << std::endl;\n   return 0;\n}\n```\n\nLet's go through the above code and explain some basic principles, slowly expanding our knowledge of taoPQ.\n\nTo use taoPQ, you include the top-level header with `#include <tao/pq.hpp>`.\nThe individual include files in `tao/pq/` are not meant to be included directly.\n\nStarting with `tao::pq::connection::create(\"dbname=template1\")`, we can see that the `tao::pq::connection` class has a static `create()`-method.\nYou provide a single parameter, the [connection string➚](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING), and the method then returns a `std::shared_ptr<tao::pq::connection>` that holds the connection object it opened.\nThe reason why connections (and several other objects in taoPQ) are handled via smart pointers will be explained later.\nNote that you can use `auto` as shown above to simplify your code.\n\nA connection has an `execute()`-method which can be used to run SQL statements.\nThe `execute()`-method returns a result object directly, meaning `tao::pq::result` objects are not handled via smart pointers.\n\nAs the statement we just executed was a `SELECT` statement, the result contains a set of rows containing the data returned from the `SELECT` statement.\nThe above shows a simple way of converting a result set that contains only a single row with a single column into a C++ `std::string`.\nOf course there are other, more sophisticated ways to retrieve and convert result data when you received multiple rows with multiple columns, those will be shown later.\n\nCongratulations, you now can write simple programs with taoPQ.\n\n## Next Steps\n\nThe following chapters are good next steps to get to know taoPQ:\n\n* The [Connection](Connection.md) chapter goes into more details on the methods offered by `tao::pq::connection`, introduces transactions, and explains why some objects in taoPQ are handled via smart pointers.\n* The [Transaction](Transaction.md) chapter explains in more detail how taoPQ handles transactions, makes sure that you don't mess up the transaction ordering and nesting, and which transaction types are supported.\n* The [Statement](Statement.md) chapter gives more information on how to send statements and parameters.\n* The [Result](Result.md) chapter explains what types of results exist, how you can access the data they contain, and how to convert results into C++ types, including containers.\n\n* The [Installation](Installation.md) chapter explains how to install taoPQ.\n* The [Requirements](Requirements.md) chapter lists our requirements and assumptions about the used server and protocol versions, encoding support, etc.\n\n## Advanced Topics\n\n* The [Error Handling](Error-Handling.md) chapter gives some general hints as to how we manage error scenarios and how those are communicated to the application.\n* The [Parameter Type Conversion](Parameter-Type-Conversion.md) chapter explains what C++ data types can be used as parameters when executing SQL statements, how NULL values are mapped to C++ data types, and how you can extend the supported types by registering your own types.\n* The [Result Type Conversion](Result-Type-Conversion.md) chapter explains what C++ data types can be extracted from results, how you can extend the supported types by registering your own types, and how to use `tao::pq::result`'s API elegantly and efficiently.\n* The [Binary Data](Binary-Data.md) chapter explains the support for PostgreSQL's [`BYTEA`➚](https://www.postgresql.org/docs/current/datatype-binary.html) data type in taoPQ and some design decisions regarding the C++ interface.\n\n* The [Connection Pool](Connection-Pool.md) chapter explains how a connection pool might help your application, especially when you use multi-threading. Our connection pool offers some novel features that ease the handling of borrowed connections significantly.\n* The [Bulk Transfer](Bulk-Transfer.md) chapter explains how we support high-speed [bulk data transfer➚](https://www.postgresql.org/docs/current/sql-copy.html) to or from the server.\n* The [Large Object](Large-Object.md) chapter provides access to PostgreSQL's [large object➚](https://www.postgresql.org/docs/current/largeobjects.html) facility.\n* The [Performance](Performance.md) chapter gives hints on how to improve your application's performance, as well as explaining some gotchas you might encounter when using taoPQ.\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Installation.md",
    "content": "# Installation\n\n## Using CMake\n\nSince CMake 3.11, the feature [FetchContent➚](https://cmake.org/cmake/help/latest/module/FetchContent.html) can be used to download and build project dependencies.\nThis mechanism makes our development much easier, but it lacks in terms of reproducibility, so be careful if you are using it for production. Also, we will use `FetchContent_MakeAvailable` which is available since CMake 3.14:\n\n```cmake\ncmake_minimum_required(VERSION 3.14)\nproject(example CXX)\n\ninclude(FetchContent)\nfind_package(PostgreSQL REQUIRED)\n\nFetchContent_Declare(\n   taocpp-taopq\n   GIT_REPOSITORY https://github.com/taocpp/taopq\n   GIT_TAG main\n)\nFetchContent_MakeAvailable(taocpp-taopq)\n\n\nadd_library(example main.cpp)\ntarget_link_libraries(example taocpp::taopq)\nset_property(TARGET example PROPERTY CXX_STANDARD 20)\n```\n\nNow, we just need to execute CMake as usual:\n\n```sh\ncmake .\ncmake --build .\n```\n\nThe CMake client will download taoPQ source files based on the `main` branch, but is highly recommended using a commit or tag to keep the reproducibility.\nBesides that, PostgreSQL (libpq) is a pre-requirement. You can extend the `CMakeLists.txt` to download and build libpq too, or just consume from your system.\nWhen executing the build step, taoPQ will be built first, as its target is required by our application, after that, the example application will be built and linked to both libpq and taoPQ.\n\n---\n\n## Using Conan\n\nYou can install pre-built binaries for taoPQ or build it from source using [Conan](https://conan.io/). Use the following command:\n\n```bash\nconan install --requires=\"taocpp-taopq/[*]\" --build=missing\n```\n\nThe taoPQ Conan recipe is kept up to date by Conan maintainers and community contributors.\nIf the version is out of date, please [create an issue or pull request](https://github.com/conan-io/conan-center-index) on the ConanCenterIndex repository.\n\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Large-Object.md",
    "content": "# Large Object\n\nPostgreSQL has a [large object➚](https://www.postgresql.org/docs/current/largeobjects.html) facility, which provides stream-style access to user data that is stored in a special large-object structure.\nStreaming access is useful when working with data values that are too large to manipulate conveniently as a whole.\n\n## Synopsis\n\n```c++\nnamespace tao::pq\n{\n   enum class oid : Oid  // Oid defined by libpq\n   {\n      invalid = InvalidOid,  // InvalidOid defined by libpq\n\n      // undisclosed additional values\n   };\n\n   class transaction;\n\n   class large_object final\n   {\n   public:\n      static auto create( const std::shared_ptr< transaction >& transaction,\n                          const oid desired_id = oid::invalid ) -> oid;\n\n      static void remove( const std::shared_ptr< transaction >& transaction,\n                          const oid id );\n\n      static auto import_file( const std::shared_ptr< transaction >& transaction,\n                               const char* filename,\n                               const oid desired_id = oid::invalid ) -> oid;\n\n      static void export_file( const std::shared_ptr< transaction >& transaction,\n                               const oid id,\n                               const char* filename );\n\n      large_object( const std::shared_ptr< transaction >& transaction,\n                    const oid id,\n                    const std::ios_base::openmode m );\n\n      large_object( const large_object& ) = delete;\n      large_object( large_object&& other ) noexcept;\n\n      ~large_object();\n\n      void operator=( const large_object& ) = delete;\n      auto operator=( large_object&& rhs ) -> large_object&;\n\n      void close();\n\n      auto read( char* data, const std::size_t size ) -> std::size_t;\n      auto read( std::byte* data, const std::size_t size ) -> std::size_t;\n\n      void write( const char* data, const std::size_t size );\n      void write( const std::byte* data, const std::size_t size );\n\n      void write( const char* data );\n\n      template< typename T = binary >\n      auto read( const std::size_t size ) -> T;\n\n      template< typename... Ts >\n      void write( Ts&&... ts );\n\n      void resize( const std::int64_t size );\n\n      auto seek( const std::int64_t offset, const std::ios_base::seekdir whence ) -> std::int64_t;\n\n      auto tell() const -> std::int64_t;\n   };\n}\n```\n\n:point_up: All large object manipulation using these functions must take place within an SQL transaction block, since large object file descriptors are only valid for the duration of a transaction.\n\n## Creating a Large Object\n\nTo [create➚](https://www.postgresql.org/docs/current/lo-interfaces.html#LO-CREATE) a new large object, call\n\n```c++\nstatic auto tao::pq::large_object::create( const std::shared_ptr<tao::pq::transaction>& transaction,\n                                           const tao::pq::oid desired_id = tao::pq::oid::invalid ) -> tao::pq::oid;\n```\n\nIf no desired oid is given, the server will return the oid that was assigned to the new large object.\nIf you specify a desired oid and that oid is already used, or if any other error occurs, an exception will be thrown.\n\n## Removing a Large Object\n\nTo [remove➚](https://www.postgresql.org/docs/current/lo-interfaces.html#LO-UNLINK) a large object from the database, call\n\n```c++\nstatic void tao::pq::large_object::remove( const std::shared_ptr<tao::pq::transaction>& transaction, const tao::pq::oid id );\n```\n\n## Importing a Large Object\n\nTo [import➚](https://www.postgresql.org/docs/current/lo-interfaces.html#LO-IMPORT) an operating system file as a large object, call\n\n```c++\nstatic auto tao::pq::large_object::import_file( const std::shared_ptr<tao::pq::transaction>& transaction,\n                                                const char* filename,\n                                                const tao::pq::oid desired_id = tao::pq::oid::invalid ) -> tao::pq::oid;\n```\n\nIf no desired oid is given, the server will return the oid that was assigned to the new large object.\nIf you specify a desired oid and that oid is already used, or if any other error occurs, an exception will be thrown.\n\n## Exporting a Large Object\n\nTo [export➚](https://www.postgresql.org/docs/current/lo-interfaces.html#LO-EXPORT) a large object into an operating system file, call\n\n```c++\nstatic void tao::pq::large_object::export_file( const std::shared_ptr<tao::pq::transaction>& transaction,\n                                                const tao::pq::oid id,\n                                                const char* filename );\n```\n\nIf an error occurs an exception will be thrown.\n\n## Opening an Existing Large Object\n\nTo [open➚](https://www.postgresql.org/docs/current/lo-interfaces.html#LO-OPEN) an existing large object for reading or writing, call\n\n```c++\ntao::pq::large_object::large_object( const std::shared_ptr<tao::pq::transaction>& transaction,\n                                     const tao::pq::oid id,\n                                     const std::ios_base::openmode m );\n```\n\nThe mode `m` bits control whether the object is opened for reading (`std::ios_base::in`), writing (`std::ios_base::out`), or both.\nIf an error occurs an exception will be thrown.\n\nThe destructor will take care of [closing➚](https://www.postgresql.org/docs/current/lo-interfaces.html#LO-CLOSE) the large object descriptor.\n\n## Writing Data to a Large Object\n\nTo [write➚](https://www.postgresql.org/docs/current/lo-interfaces.html#LO-WRITE) data to a large object, several methods are available.\n\n```c++\nvoid tao::pq::large_object::write( const char* data, const std::size_t size );\nvoid tao::pq::large_object::write( const std::byte* data, const std::size_t size );\n\nvoid tao::pq::large_object::write( const char* data );\n\ntemplate< typename... Ts >\nvoid tao::pq::large_object::write( Ts&&... ts );\n```\n\nThe first three methods write a chunk of data starting at `data` that is `size` bytes long to the large object.\n\nThe fourth method expects a zero-terminated string, which will be written to the large object.\n\nThe fifth method template forwards its arguments to a call to [`tao::pq::to_binary_view()`](Binary-Data.md), then writes the binary data to the large object.\nThis allows all data types that are accepted by `tao::pq::to_binary_view()` to be written seamlessly into large objects.\n\nIf an error occurs an exception will be thrown.\n\n## Reading Data from a Large Object\n\nTo [read➚](https://www.postgresql.org/docs/current/lo-interfaces.html#LO-READ) data from a large object, several methods are available.\n\n```c++\nauto tao::pq::large_object::read( char* data, const std::size_t size ) -> std::size_t;\nauto tao::pq::large_object::read( std::byte* data, const std::size_t size ) -> std::size_t;\n\ntemplate< typename T = tao::pq::binary >\nauto tao::pq::large_object::read( const std::size_t size ) -> T;\n```\n\nThe first three methods read up to `size` bytes from the large object into `data`.\nThe methods will return the number of bytes actually read; this will be less than `size` if the end of the large object is reached first.\n\nThe fourth method will create a new object of type `T` and read up to `size` bytes from the large object.\n`T` must be one of the following types:\n\n* `std::string`\n* [`tao::pq::binary`](Binary-Data.md) aka `std::vector<std::byte>`\n\n:point_up: Although the `size` parameter of the above methods is declared as `std::size_t`, the methods will reject values larger than `INT_MAX`.\nIn practice, it's best to transfer data in chunks of at most a few megabytes anyway.\n\nIf an error occurs an exception will be thrown.\n\n## Seeking in a Large Object\n\nTo change the [current read or write➚](https://www.postgresql.org/docs/current/lo-interfaces.html#LO-SEEK) location associated with a large object, call\n\n```c++\nauto tao::pq::large_object::seek( const std::int64_t offset, const std::ios_base::seekdir whence ) -> std::int64_t;\n```\n\nThis method moves the current location pointer for the large object to the new location specified by `offset`.\nThe valid values for `whence` are `std::ios_base::beg` (seek from object start), `std::ios_base::cur` (seek from current position), and `std::ios_base::end` (seek from object end).\nThe return value is the new location pointer.\n\nIf an error occurs an exception will be thrown.\n\n## Obtaining the Seek Position of a Large Object\n\nTo [obtain➚](https://www.postgresql.org/docs/current/lo-interfaces.html#LO-TELL) the current read or write location of a large object, call\n\n```c++\nauto tao::pq::large_object::tell() const -> std::int64_t;\n```\n\nIf an error occurs an exception will be thrown.\n\n## Truncating a Large Object\n\nTo [truncate➚](https://www.postgresql.org/docs/current/lo-interfaces.html#LO-TRUNCATE) a large object to a given size, call\n\n```c++\nvoid tao::pq::large_object::resize( const std::int64_t size );\n```\n\nIf `size` is greater than the large object's current length, the large object is extended to the specified length with null bytes ('\\0').\n\nIf an error occurs an exception will be thrown.\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Parameter-Type-Conversion.md",
    "content": "# Parameter Type Conversion\n\nWhen [executing statements](Statement.md), you can pass any number of parameters after the statement itself to the `execute()`-method.\nEach parameter then gets converted through the parameter traits class template into one or more positional parameters for the SQL statement.\nBy default, the following C++ types are available for use as parameters.\n\n## NULL\n\nIf you want to pass NULL to the database, you pass `tao::pq::null`.\n\n## Fundamental Types\n\n* Booleans\n  * `bool`\n* Character\n  * `char`\n* Integral Types\n  * `signed char` (8-bit signed integer)\n  * `unsigned char` (8-bit unsigned integer)\n  * `short`\n  * `unsigned short`\n  * `int`\n  * `unsigned int`\n  * `long`\n  * `unsigned long`\n  * `long long`\n  * `unsigned long long`\n* Floating Point Types\n  * `float`\n  * `double`\n  * `long double`\n* Strings\n  * `const char*`\n  * `std::string`\n  * `std::string_view`\n* [Binary](Binary-Data.md) ([`BYTEA`➚](https://www.postgresql.org/docs/current/datatype-binary.html))\n  * `std::vector< std::byte >`\n  * `std::span< std::byte >`\n* [`ARRAY`➚](https://www.postgresql.org/docs/current/arrays.html)\n  * `std::array< T, N >`\n  * `std::list< T >`\n  * `std::set< T >`\n  * `std::unordered_set< T >`\n  * `std::vector< T >`\n\n## `std::optional< T >`\n\nRepresents a [nullable➚](https://en.wikipedia.org/wiki/Nullable_type) type.\n\nIf the optional is not empty, then the parameters from `T` are generated.\nIf the optional is empty, it is equivalent to one or more `tao::pq::null` parameter(s).\nThe number of NULL values generated depends on the number of parameters that `T` would generate.\n\n## `std::pair< T, U >`\n\nGenerates all parameters from `T`, then all parameters from `U`, in that order.\nNote that this generates at least two parameters, possibly more.\nPairs can be nested, e.g. `std::pair<std::pair<int,int>,int>` would generate three parameters.\n\n## `std::tuple< Ts... >`\n\nAs a generalisation of pairs, tuples generate all parameters for their individual elements, in order.\n\n## Aggregates\n\nAny suitable aggregate data type can be used as a parameter when registered with taoPQ.\n\n```c++\nstruct my_aggregate\n{\n   std::string name;\n   unsigned age;\n   std::string address;\n   bool is_pet_owner;\n};\n\ntemplate<>\ninline constexpr bool tao::pq::is_aggregate< my_aggregate > = true;\n```\n\nSee [Aggregate Support](Aggregate.md) for more information.\n\n## Custom Data Types\n\nCustom data types can be registered in two different ways, by using a `to_taopq()` function or method, or by specializing the `tao::pq::parameter_traits` class template.\n\n### `to_taopq()`\n\nYou can use a function or method called `to_taopq()`, any value returned will then be fed into the parameters as outlined above.\nUsually, that means a simple conversion will return a single known type, more complicated types return a `std::tuple` to return multiple parameters for the SQL statement.\nThere are multiple places where this function or method can be placed.\n\n#### Intrusive Placement\n\nIf you have control over a class type, you can add a method called `to_taopq()` that can be called with no parameters.\nThe method can be marked `const` and/or `noexcept` as applicable.\n\n```c++\nclass my_int_wrapper\n{\nprivate:\n   int value;\n\npublic:\n   explicit my_int_wrapper( int v ) : value( v ) {}\n\n   auto to_taopq() const noexcept\n   {\n      return value;\n   }\n};\n```\n\nYou can now pass values of type `my_int_wrapper` as parameters to call taoPQ's `execute()`-methods.\n\nIf your class has more members, you can return multiple values:\n\n```c++\nclass my_coordinates\n{\nprivate:\n   double x,y,z;\n\npublic:\n   //  ctors, etc.\n\n   auto to_taopq() const noexcept\n   {\n      return std::tie( x, y, z );\n   }\n};\n```\n\nThe above means that each time you pass a `my_coordinates` instance as a parameter to an `execute()`-method, three positional parameters are added and can be referenced from the SQL statement.\n\n#### Non-Intrusive Placement\n\nIf you can't modify the class you could specialize `tao::pq::bind<...>` and place a static `to_taopq()`-method inside the specialization, or provide a free function called `to_taopq()` instead.\nThose functions must accept a single parameter of the class you want to register.\n\nExample for the specialization of `tao::pq::bind<...>`:\n\n```c++\nstruct some_coordinates\n{\n   double x,y,z;\n};\n\ntemplate<>\nstruct tao::pq::bind< some_coordinates >\n{\n   static auto to_taopq( const some_coordinates& v ) noexcept\n   {\n      return std::tie( v.x, v.y, v.z );\n   }\n};\n```\n\nExample for the free function:\n\n```c++\nstruct some_coordinates\n{\n   double x,y,z;\n};\n\nauto to_taopq( const some_coordinates& v ) noexcept\n{\n   return std::tie( v.x, v.y, v.z );\n}\n```\n\nThe free function is found either by [ADL➚](https://en.cppreference.com/w/cpp/language/adl) or in namespace `tao::pq`.\n\n:point_up: Note that any returned value in the above examples can itself be a registered custom type.\ntaoPQ will simply expand parameters recursively.\n\n### `tao::pq::parameter_traits< T >`\n\nIf the above custom data type registration via `to_taopq()` is somehow not sufficient, you can specialize the `tao::pq::parameter_traits` class template.\nFor now please consult the source code or ask the developers.\n\nTODO: Write proper documentation.\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Performance.md",
    "content": "# Performance\n\n**TODO**\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Requirements.md",
    "content": "# Requirements\n\n## Operating System Support\n\n* We support:\n  * [Windows➚](https://en.wikipedia.org/wiki/Microsoft_Windows).\n  * [macOS➚](https://en.wikipedia.org/wiki/MacOS).\n  * [Linux➚](https://en.wikipedia.org/wiki/Linux).\n* Other systems might work.\n\n## Compiler Support\n\n* We support:\n  * [Visual Studio➚](https://en.wikipedia.org/wiki/Microsoft_Visual_Studio) version 2022 or newer.\n  * [Xcode➚](https://en.wikipedia.org/wiki/Xcode) version 15 or newer.\n  * [GCC➚](https://gcc.gnu.org/) version 13 or newer.\n  * [Clang➚](https://clang.llvm.org/) version 16 or newer.\n* Other compilers might work.\n\n## Language Requirements\n\n* We require [C++20➚](https://en.wikipedia.org/wiki/C%2B%2B20) or newer.\n* We require exception support. The `-fno-exceptions` option is not supported.\n* We require RTTI support. The `-fno-rtti` option is not supported.\n\n## Compiler Options/Warnings\n\n* We support Clang's [`-fms-extensions`➚](https://clang.llvm.org/docs/MSVCCompatibility.html) option.\n* We support the `/W4` option on [Visual Studio➚](https://docs.microsoft.com/en-us/cpp/build/reference/compiler-option-warning-level).\n* We support the `-pedantic`, `-Wall`, and `-Wextra` options on [GCC➚](https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html) and [Clang➚](https://clang.llvm.org/docs/DiagnosticsReference.html).\n\n:point_up: Note that we *support* these options, we don't *require* them to be used.\nYou can decide which options you want to use in your project, we just try to not get in the way by making sure that our code doesn't generate any of those warnings.\n\n## Database Requirements\n\n* We expect the database to use UTF-8 encoding.\n* We expect the database to send `BYTEA` data in [`bytea` hex format➚](https://www.postgresql.org/docs/current/datatype-binary.html).\n* We expect the database connection to use [protocol version 3➚](https://www.postgresql.org/docs/current/protocol.html).\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Result-Type-Conversion.md",
    "content": "# Result Type Conversion\n\nDepending on what type of [statement](Statement.md) was executed, you receive a [result](Result.md) containing a query result set.\nThose results then offer conversions to C++ data types through various methods, e.g. `tao::pq::row::as<T>()`.\nThis chapter discusses the data types that are available by default and how to register your own custom data types when needed.\n\n## Fundamental Types\n\nBy default, the following C++ types are available for use as result types.\n\n* Booleans\n  * `bool`\n* Character\n  * `char`\n* Integral Types\n  * `signed char` (8-bit signed integer)\n  * `unsigned char` (8-bit unsigned integer)\n  * `short`\n  * `unsigned short`\n  * `int`\n  * `unsigned int`\n  * `long`\n  * `unsigned long`\n  * `long long`\n  * `unsigned long long`\n* Floating Point Types\n  * `float`\n  * `double`\n  * `long double`\n* Strings\n  * `const char*`\n  * `std::string`\n  * `std::string_view`\n* [Binary](Binary-Data.md) ([`BYTEA`➚](https://www.postgresql.org/docs/current/datatype-binary.html))\n  * `std::vector< std::byte >`\n* [`ARRAY`➚](https://www.postgresql.org/docs/current/arrays.html)\n  * `std::list< T >`\n  * `std::set< T >`\n  * `std::unordered_set< T >`\n  * `std::vector< T >`\n\n## `std::optional< T >`\n\nRepresents a [nullable➚](https://en.wikipedia.org/wiki/Nullable_type) type.\n\nIf the result field is NULL, an empty optional is returned.\nIf the result field is not NULL, the value is converted to `T` and returned in the optional.\n\n## `std::pair< T, U >`\n\nReturns a `std::pair< T, U >`, hence convertes two (or more) neighboring fields from the result's row.\nIt can read more than two fields when `T` or `U` themselves read more than one field, i.e. converting to `std::pair<int,std::pair<int,int>>` will read three fields from the result's row.\n\n## `std::tuple< Ts... >`\n\nAs a generalisation of pairs, tuples read all fields for their individual elements, in order.\n\n## Aggregates\n\nAny suitable aggregate data type can be used as a result type when registered with taoPQ.\n\n```c++\nstruct my_aggregate\n{\n   std::string name;\n   unsigned age;\n   std::string address;\n   bool is_pet_owner;\n};\n\ntemplate<>\ninline constexpr bool tao::pq::is_aggregate< my_aggregate > = true;\n```\n\nSee [Aggregate Support](Aggregate-Support.md) for more information.\n\n## Custom Data Types\n\nCustom data types can be registered in two different ways, by using a `from_taopq()` function or method, or by specializing the `tao::pq::result_traits` class template.\n\n### `from_taopq()`\n\nYou can use a function or method called `from_taopq()`, which takes one or more suitable parameters and returns a new instance of your type.\nThere are multiple places where this function or method can be placed.\n\n#### Intrusive Placement\n\nIf you have control over a class type, you can add a static method called `from_taopq()`.\nThe method can be marked `noexcept` if applicable.\n\n```c++\nclass my_int_wrapper\n{\nprivate:\n   int value;\n\npublic:\n   explicit my_int_wrapper( int v ) : value( v ) {}\n\n   static auto from_taopq( const int v ) noexcept\n   {\n      return my_int_wrapper( v );\n   }\n};\n```\n\ntaoPQ will find the class' method, analyze the methods parameters and convert the result's row accordingly.\nThe method must have at least one parameter, each parameter will read the required number of fields.\n\n#### Non-Intrusive Placement\n\nIf you can't modify the class you could specialize `tao::pq::bind<...>` and place a static `from_taopq()`-method inside the specialization.\nThe method behaves identical to the intrusive version.\n\nExample for the specialization of `tao::pq::bind<...>`:\n\n```c++\nstruct some_coordinates\n{\n   double x,y,z;\n};\n\ntemplate<>\nstruct tao::pq::bind< some_coordinates >\n{\n   static auto from_taopq( const double x, const double y, const double z ) noexcept\n   {\n      return some_coordinates{ x, y, z };\n   }\n};\n```\n\n:point_up: Note that unlike [`to_taopq()`](Parameter-Type-Conversion.md), there is no free function version for `from_taopq()` available.\nThis is due to the fact that the custom data type is not a parameter, but rather the returned value.\nThe parameter list can therefore be identical for multiple custom data types and this could leads to conflicting overloads.\nAlso, [ADL➚](https://en.cppreference.com/w/cpp/language/adl) would be unavailable as only the parameters' types are considered for ADL.\n\n### `tao::pq::result_traits< T >`\n\nIf the above custom data type registration via `from_taopq()` is somehow not sufficient, you can specialize the `tao::pq::result_traits` class template.\nFor now please consult the source code or ask the developers.\n\nTODO: Write proper documentation.\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Result.md",
    "content": "# Result\n\nWhen [executing statements](Statement.md) you receive a result object.\nA result comes in two flavours, depending on what statement was executed.\n\nWhen executing a [query statement➚](https://www.postgresql.org/docs/current/queries-overview.html), the database returns a result set, i.e. any number of rows containing one or more fields of data.\n\nWhen executing non-query statements, you can usually only extract the number of affected rows.\n\nQuery results can be iterated or conveniently converted into a C++ data structure.\nPredefined types include most arithmetic C++ data types, STL containers, `std::pair`/`std::tuple`, and `std::optional` for [nullable➚](https://en.wikipedia.org/wiki/Nullable_type) values.\nAgain custom types can be added with custom conversion functions.\n\n## Synopsis\n\nDon't be intimidated by the size of the API, as you can see several methods are just single-line convenience forwarders.\n\nWe will first give the synopsis of everything, afterwards we will break down the API into small logical portions.\n\n```c++\nnamespace tao::pq\n{\n   namespace internal\n   {\n      class zsv;  // zero-terminated string view\n   }\n\n   using null_t = decltype( null );\n\n   class row;\n   class field;\n\n   class result final\n   {\n   private:\n      // satisfies LegacyRandomAccessIterator, see\n      // https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator\n      class const_iterator;\n\n   public:\n      // non-query result access\n      bool has_rows_affected() const noexcept;\n      auto rows_affected() const -> std::size_t;\n\n      // information about the returned fields\n      auto columns() const noexcept -> std::size_t;\n\n      auto name( const std::size_t column ) const -> std::string;\n      auto index( const internal::zsv in_name ) const -> std::size_t;\n\n      // size of the result set\n      bool empty() const;\n      auto size() const -> std::size_t;\n\n      // iteration\n      auto begin() const -> const_iterator;\n      auto end() const -> const_iterator;\n\n      auto cbegin() const -> const_iterator;\n      auto cend() const -> const_iterator;\n\n      // get basic information about a field\n      bool is_null( const std::size_t row, const std::size_t column ) const;\n      auto get( const std::size_t row, const std::size_t column ) const -> const char*;\n\n      // access rows\n      auto operator[]( const std::size_t row ) const noexcept -> pq::row;\n      auto at( const std::size_t row ) const -> pq::row;\n\n      // convenience conversions for whole result sets\n\n      // expects size()==1, converts the only row to T\n      template< typename T >\n      auto as() const -> T;\n\n      template< typename T >\n      auto optional() const -> std::optional< T >;\n\n      // convenience conversions to pair/tuple\n      template< typename T, typename U >\n      auto pair() const\n      {\n         return as< std::pair< T, U > >();\n      }\n\n      template< typename... Ts >\n      auto tuple() const\n      {\n         return as< std::tuple< Ts... > >();\n      }\n\n      // convert each row into T::value_type and add to a container of type T\n      template< typename T >\n      auto as_container() const -> T;\n\n      // convenience conversions to standard containers\n      template< typename... Ts >\n      auto vector() const\n      {\n         return as_container< std::vector< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto list() const\n      {\n         return as_container< std::list< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto set() const\n      {\n         return as_container< std::set< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto multiset() const\n      {\n         return as_container< std::multiset< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto unordered_set() const\n      {\n         return as_container< std::unordered_set< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto unordered_multiset() const\n      {\n         return as_container< std::unordered_multiset< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto map() const\n      {\n         return as_container< std::map< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto multimap() const\n      {\n         return as_container< std::multimap< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto unordered_map() const\n      {\n         return as_container< std::unordered_map< Ts... > >();\n      }\n\n      template< typename... Ts >\n      auto unordered_multimap() const\n      {\n         return as_container< std::unordered_multimap< Ts... > >();\n      }\n\n      // access underlying result pointer from libpq\n      auto underlying_raw_ptr() noexcept -> PGresult*;\n      auto underlying_raw_ptr() const noexcept -> const PGresult*;\n   };\n\n   class row\n   {\n   private:\n      // satisfies LegacyRandomAccessIterator, see\n      // https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator\n      class const_iterator;\n\n   public:\n      auto slice( const std::size_t offset, const std::size_t in_columns ) const -> row;\n\n      auto columns() const noexcept -> std::size_t;\n\n      auto name( const std::size_t column ) const -> std::string;\n      auto index( const internal::zsv in_name ) const -> std::size_t;\n\n      // iteration\n      auto begin() const -> const_iterator;\n      auto end() const -> const_iterator;\n\n      auto cbegin() const -> const_iterator;\n      auto cend() const -> const_iterator;\n\n      bool is_null( const std::size_t column ) const;\n      auto get( const std::size_t column ) const -> const char*;\n\n      template< typename T >\n      auto get( const std::size_t column ) const -> T;\n\n      template< typename T >\n      auto optional( const std::size_t column ) const\n      {\n         return get< std::optional< T > >( column );\n      }\n\n      template< typename T >\n      auto as() const -> T;\n\n      template< typename T >\n      auto optional() const\n      {\n         return as< std::optional< T > >();\n      }\n\n      template< typename T, typename U >\n      auto pair() const\n      {\n         return as< std::pair< T, U > >();\n      }\n\n      template< typename... Ts >\n      auto tuple() const\n      {\n         return as< std::tuple< Ts... > >();\n      }\n\n      auto at( const std::size_t column ) const -> field;\n      auto operator[]( const std::size_t column ) const noexcept -> field;\n\n      auto at( const internal::zsv in_name ) const -> field;\n      auto operator[]( const internal::zsv in_name ) const -> field;\n\n      friend void swap( row& lhs, row& rhs ) noexcept;\n   };\n\n   class field\n   {\n   public:\n      auto name() const -> std::string;\n      auto index() const -> std::size_t;\n\n      bool is_null() const;\n      auto get() const -> const char*;\n\n      template< typename T >\n      auto as() const -> T;\n\n      template< typename T >\n      auto optional() const\n      {\n         return as< std::optional< T > >();\n      }\n   };\n\n   bool operator==( const field& f, null_t )\n   {\n      return f.is_null();\n   }\n\n   bool operator==( null_t, const field& f )\n   {\n      return f.is_null();\n   }\n\n   bool operator!=( const field& f, null_t )\n   {\n      return !f.is_null();\n   }\n\n   bool operator!=( null_t, const field& f )\n   {\n      return !f.is_null();\n   }\n}\n```\n\n## Non-Query Results\n\nFor non-query results, i.e. when you called an `INSERT`-, `UPDATE`-, or `DELETE`-statement, you really only need the `rows_affected()`-method.\nIn generic programming, when you might not know what kind of result you have, you can check whether or not a result is a non-query result by calling the `has_rows_affected()`-method.\n\n```c++\nbool tao::pq::result::has_rows_affected() const;\nauto tao::pq::result::rows_affected() const -> std::size_t;\n```\n\n## Query Results\n\n[Query results➚](https://www.postgresql.org/docs/current/queries-overview.html) are non-mutable data sets, they are cheap to copy, move, or assign and you can iterate over the data multiple times in random order.\nLikewise, rows are also non-mutable, as well as fields.\nThis also means iterators will behave as constant iterators.\n\nQuery results act similar to a random-access container.\nThe don't fully implement the [container requirements➚](https://en.cppreference.com/w/cpp/named_req/Container), but a reasonable subset of those are provided.\n\n:point_up: Rows and fields are non-owning, meaning they are only valid as long as the query result instance is still valid.\n\n### Basics\n\nYou can query the container's size, i.e. the number of rows it contains, by calling the `size()`-method.\nThe `empty()`-method will, of course, return whether the size of the container is zero or not.\n\n```c++\nbool tao::pq::result::empty() const;\nauto tao::pq::result::size() const -> std::size_t;\n```\n\nThe number of columns, column order, and the column name is the same for all rows of a result set.\nYou can query the number of columns by calling the `columns()`-method.\nYou can retrieve the name of a column using the `name()`-method, or the column index by using the `index()`-method.\n\n```c++\nauto tao::pq::result::columns() const -> std::size_t;\nauto tao::pq::result::name( std::size_t column ) const -> std::string;\nauto tao::pq::result::index( tao::pq::internal::zsv name ) const -> std::size_t;\n```\n\nDirect access to the data is provided by the `is_null()`- and the `get()`-methods.\nThe latter returns the raw string as returned by `libpq`, it is a low level access method that is rarely used directly.\n\n```c++\nbool tao::pq::result::is_null( std::size_t row, std::size_t column ) const;\nauto tao::pq::result::get( std::size_t row, std::size_t column ) const -> const char*;\n```\n\n### Row Access\n\nYou can iterate over the container's elements, the rows, with the usual methods.\nThis is what the `begin()`- and `end()`-methods are for, also allowing for the convenient use of [range-based for loops➚](https://en.cppreference.com/w/cpp/language/range-for).\n\n```c++\nauto tao::pq::result::begin() const -> tao::pq::result::const_iterator;\nauto tao::pq::result::end() const -> tao::pq::result::const_iterator;\n```\n\nThe identical `cbegin()`- and `cend()`-methods are provided for completeness.\n\nHere's an example of how to iterate all rows:\n\n```c++\nconst tao::pq::result result = ...;\nfor( const auto& row : result ) {\n   // use row to access your data\n}\n```\n\nor more traditionally:\n\n```c++\nconst tao::pq::result result = ...;\nfor( auto it = std::begin( result ); it != std::end( result ); ++it ) {\n   // use *it to access your row's data\n}\n```\n\nAlternatively, you can use an index to access the rows.\n\n```c++\nconst tao::pq::result result = ...;\nfor( std::size_t i = 0; i < result.size(); ++i ) {\n   // use result[ i ] or result.at( i ) to access your row's data\n}\n```\n\nThis is enabled by the accessors, the `at()`-method and the `[]`-operator.\n\n```c++\nauto tao::pq::result::at( std::size_t index ) const -> tao::pq::row;\nauto tao::pq::result::operator[]( std::size_t index ) const noexcept -> tao::pq::row;\n```\n\nMore conversion methods will be discussed later, after we covered the basics for rows and fields.\n\n### Field Access\n\nGiven a row, you can query information about the fields with the same methods as for the result itself.\n\n```c++\nauto tao::pq::row::columns() const -> std::size_t;\nauto tao::pq::row::name( std::size_t column ) const -> std::string;\nauto tao::pq::row::index( tao::pq::internal::zsv name ) const -> std::size_t;\n```\n\nDirect access to the data is provided by the `is_null()`- and the `get()`-methods.\nThe latter returns the raw string as returned by `libpq`, it is a low level access method that is rarely used directly.\n\n```c++\nbool tao::pq::row::is_null( std::size_t column ) const;\nauto tao::pq::row::get( std::size_t column ) const -> const char*;\n```\n\nYou can iterate over the row's elements, the fields, with the usual methods.\nThis is what the `begin()`- and `end()`-methods are for, also allowing for the convenient use of [range-based for loops➚](https://en.cppreference.com/w/cpp/language/range-for).\n\n```c++\nauto tao::pq::row::begin() const -> tao::pq::row::const_iterator;\nauto tao::pq::row::end() const -> tao::pq::row::const_iterator;\n```\n\nThe identical `cbegin()`- and `cend()`-methods are provided for completeness.\n\nHere's an example of how to iterate all fields:\n\n```c++\nconst tao::pq::result result = ...;\nfor( const auto& row : result ) {\n   for( const auto& field : row ) {\n      // use field to access your data\n   }\n}\n```\n\nor more traditionally:\n\n```c++\nconst tao::pq::result result = ...;\nfor( auto it = std::begin( result ); it != std::end( result ); ++it ) {\n   for( auto jt = std::begin( *it ); jt != std::end( *it ); ++jt ) {\n      // use *jt to access your fields's data\n   }\n}\n```\n\nAlternatively, you can use an index to access the fields.\n\n```c++\nconst tao::pq::result result = ...;\nfor( std::size_t i = 0; i < result.size(); ++i ) {\n   for( std::size_t j = 0; j < result[ i ].columns(); ++j ) {\n      // use result[ i ][ j ] or result.at( i ).at( j ) to access your field's data\n   }\n}\n```\n\nThis is enabled by the accessors, the `at()`-method and the `[]`-operator.\n\n```c++\nauto tao::pq::row::at( std::size_t index ) const -> tao::pq::field;\nauto tao::pq::row::operator[]( std::size_t index ) const noexcept -> tao::pq::field;\n```\n\nMore conversion methods will be discussed later, after we covered the basics for fields.\n\n### Fields\n\nYou can query a field's name by calling the `name()`-method.\n\n```c++\nauto tao::pq::field::name() const -> std::string;\n```\n\nDirect access to the data is provided by the `is_null()`- and the `get()`-methods.\nThe latter returns the raw string as returned by `libpq`, it is a low level access method that is rarely used directly.\n\n```c++\nbool tao::pq::field::is_null() const;\nauto tao::pq::field::get() const -> const char*;\n```\n\nNow that we covered the basics, we can retrieve the actual data and convert it to the data types we need.\n\n## Field Data Conversion\n\nA field can be converted to any data type `T` that is a single field wide.\nWhat we mean by that is, that `tao::pq::result_traits_size< T >` yields 1.\nThis is the case for `const char*`, `std::string`, `int`, etc.\n\nIn order to convert a field to the data type you want, you can use the `as()`-method.\n\n```c++\ntemplate< typename T >\nauto tao::pq::field::as() const -> T;\n```\n\nThe conversion is handled by the `tao::pq::result_traits` class template, which is documented in the [result type conversion](Result-Type-Conversion.md) chapter.\n\nA field also has a convenience method to convert directly into a `std::optional<T>`.\n\n```c++\ntemplate< typename T >\nauto tao::pq::field::optional() const\n{\n   return as< std::optional< T > >();\n}\n```\n\n## Row Data Conversion\n\n**TODO** Finish this up for rows and results...\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/Statement.md",
    "content": "# Statement\n\n:warning: Before showing you how to execute statements with taoPQ, we'd like to take a moment to talk about [SQL injection➚](https://en.wikipedia.org/wiki/SQL_injection).\nSQL injection is a large family of security issues that has plagued the internet for decades.\nWe designed taoPQ to allow you to *safely* and *conveniently* write code that does not allow SQL injection.\nWith that said, let's start executing statements with taoPQ.\n\n## `execute()`\n\nAll statements are executed by calling an `execute()`-method, either on a transaction, connection, or connection pool directly.\nThe synopsis of the execute methods of those types (`Type` being `tao::pq::transaction`, `tao::pq::connection`, or `tao::pq::connection_pool`) is:\n\n```c++\ntemplate< typename... As >\nauto Type::execute( const tao::pq::internal::zsv statement, As&&... as ) -> tao::pq::result;\n```\n\nFor the purpose of this chapter, it makes no difference which `Type` is used.\nThe method takes the statement itself as its first parameter plus additional parameters that will be passed to the statement.\nIt returns a `tao::pq::result` that is documented in the [Result](Result.md) chapter.\nIn case of an error, i.e. if the statement execution failed, an exception is thrown as documented in the [Error Handling](Error-Handling.md) chapter.\n\n### `tao::pq::internal::zsv`\n\nThe `tao::pq::internal::zsv` type (zero-terminated string view) that is used for the `statement` parameter ensures that you pass a zero-terminated string.\nIt is a non-owning type which has non-explicit constructor overloads for `const char*` and `const std::string&`.\nFor safety reasons, there is another overload for `std::nullptr_t` that is deleted.\nNote that we do not accept `std::string_view`, as the underlying C-API of `libpq` requires a zero-terminated string.\n\n## Positional Parameters\n\nTo reference the parameters you supplied in the statement, you use [positional parameters➚](https://www.postgresql.org/docs/current/sql-expressions.html).\nPositional parameters are of the form `$n` where `n` is a number starting at 1.\nHere's an example of how you can insert a row with two columns into the database:\n\n```c++\ntr->execute( \"INSERT INTO user ( name, age ) VALUES ( $1, $2 )\", \"Daniel\", 42 );\n```\n\nThe actual data is separated from the statement itself.\nThe use of positional parameters makes passing strings and other types safe, as there is no need for manually escaping the data.\nOur library now knows what is the actual SQL statement you want to send and what is the data you want to send.\nThis protects you from SQL injections and it is also quiet convenient.\nThe way the data is now transferred between the client and the server is also more efficient.\n\n:warning: The only thing you have to remember is to **never** concatenate strings together to create the SQL statement including the data manually.\nAs this is such an important point, we will illustrate how it should **not** be done:\n\n```c++\nauto find_user( const std::string& name )\n{\n   return connection->execute( \"SELECT FROM user WHERE name = '\" + name + \"'\" );\n   //                                                         ~~~~~~~~~~~~~~       WRONG!!!\n}\n```\n\n:no_entry: Never concatenate SQL statements manually!\nConsider `name` to be an input field coming from untrusted user input.\nWhat happens, if a user enters the following \"name\":\n\n`Robert'; DELETE FROM user WHERE name <> 'Little Bobby Tables`\n\nYupp, all users other than [`Little Bobby Tables`➚](https://xkcd.com/327/) have just been deleted from the database.\n\nYou might have seen other libraries where you should [escape the data explicitly➚](https://www.postgresql.org/docs/current/libpq-exec.html#LIBPQ-EXEC-ESCAPE-STRING), something like:\n\n```c++\nauto find_user( const std::string& name )\n{\n   return connection->execute( \"SELECT FROM user WHERE name = '\" + connection->escape( name ) + \"'\" );\n}\n```\n\nThis is cumbersome and error-prone.\nIt is easy to forget calling the escape method, which the compiler will *not* catch for you, and for non-strings, the code needs to call explicit conversion methods to string.\nThis turns longer SQL statements into a long, ugly mess.\n\nPositional parameters solve all of those problems and therefore taoPQ does not even offer any escaping methods.\nTo be safe and to make your life easier, with taoPQ always use positional parameters:\n\n```c++\nauto find_user( const std::string& name )\n{\n   return connection->execute( \"SELECT FROM user WHERE name = $1\", name );\n}\n```\n\n## Multi-Query Commands\n\nSome SQL client libraries allow multi-query commands, i.e. the command string can include multiple SQL statements.\nIn `libpq`, this is supported by the [`PQexec()`➚](https://www.postgresql.org/docs/current/libpq-exec.html)-function.\nAs an extra defense against SQL injection, taoPQ *never* calls the `PQexec()`-function.\nWe allow at most one SQL command in the given statement passed to an `execute()`-method.\n\n## Prepared Statements\n\nIn the [Connection](Connection.md) chapter we have shown how you can prepare (and deallocate) prepared statements.\nIn order to execute prepared statements, you simply pass the name of the prepared statement to the `execute()`-method instead of the actual SQL statement.\nThis might look like this:\n\n```c++\nconnection->prepare( \"insert_user\", \"INSERT INTO user ( name, age ) VALUES ( $1, $2 )\" );\n\nconnection->execute( \"insert_user\", \"Daniel\", 42 );\nconnection->execute( \"insert_user\", \"Tom\", 41 );\nconnection->execute( \"insert_user\", \"Jerry\", 29 );\n```\n\nThis is both more efficient and also allows you to change the statements in a central place if need be, without touching any of the places where it is actually used.\n\nYou might want to wrap calls to a (prepared) statement into an application-specific wrapper, that way you add C++'s type safety for the rest of the application calling that method (and also receiving the result).\n\nAnother idea that we already used in practice is to read the SQL statements from a configuration file.\nWhen you then need to change a statement, e.g. to work around a performance issue with the database or because you renamed a column in the database, all you need to do is adapt the configuration.\nNo need to recompile the application.\n\n## Type Conversion\n\nThe above example also shows that you can use different data types as parameters.\nBesides basic types like strings or integers, you can also use more complex types like `std::tuple` which will decay into multiple parameters.\nYou can even register your own data types to generate one or more parameters from them.\nThe [Parameter Type Conversion](Parameter-Type-Conversion.md) chapter explains which data types are supported out-of-the-box, and how you can register your own custom data types.\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "doc/TOC.md",
    "content": "# Table of Content\n\n* [Requirements](Requirements.md)\n  * [Operating System Support](Requirements.md#operator-system-support)\n  * [Compiler Support](Requirements.md#compiler-support)\n  * [Language Requirements](Requirements.md#language-requirements)\n  * [Compiler Options/Warnings](Requirements.md#compiler-optionswarnings)\n  * [Database Requirements](Requirements.md#database-requirements)\n* [Installation](Installation.md)\n  * **TODO** after properly rewriting this chapter\n* [Getting Started](Getting-Started.md)\n  * [Next Steps](Getting-Started.md#next-steps)\n  * [Advanced Topics](Getting-Started.md#advanced-topics)\n* [Connection Pool](Connection-Pool.md)\n  * [Synopsis](Connection-Pool.md#synopsis)\n  * [Creating Connection Pools](Connection-Pool.md#creating-connection-pools)\n  * [Borrowing Connections](Connection-Pool.md#borrowing-connections)\n  * [Executing Statements](Connection-Pool.md#executing-statements)\n  * [Cleanup](Connection-Pool.md#cleanup)\n  * [Thread Safety](Connection-Pool.md#thread-safety)\n* [Connection](Connection.md)\n  * [Synopsis](Connection.md#synopsis)\n  * [Creating a Connection](Connection.md#creating-a-connection)\n  * [Creating Transactions](Connection.md#creating-transactions)\n    * [Creating a \"Direct\" Transaction](Connection.md#creating-a-direct-transaction)\n    * [Creating a Database Transaction](Connection.md#creating-a-database-transaction)\n  * [Executing Statements](Connection.md#executing-statements)\n  * [Prepared Statements](Connection.md#prepared-statements)\n    * [Manually Prepared Statements](Connection.md#manually-prepared-statements)\n  * [Checking Status](Connection.md#checking-status)\n  * [Notification Framework](Connection.md#notification-framework)\n    * [Sending Messages](Connection.md#sending-messages)\n    * [Receiving Messages](Connection.md#receiving-messages)\n    * [Handling Messages](Connection.md#handling-messages)\n    * [Per Channel Handlers](Connection.md#per-channel-handlers)\n    * [Asynchronous Notifications](Connection.md#asynchronous-notifications)\n    * [Event Loop](Connection.md#event-loop)\n  * [Underlying Connection Pointer](Connection.md#underlying-connection-pointer)\n  * [Error Messages](Connection.md#error-messages)\n* [Transaction](Transaction.md)\n  * [Synopsis](Transaction.md#synopsis)\n  * [Creating Transactions](Transaction.md#creating-transactions)\n  * [Statement Execution](Transaction.md#statement-execution)\n  * [Terminate Transaction](Transaction.md#terminate-transaction)\n    * [Commit a Transaction](Transaction.md#commit-a-transaction)\n    * [Abort a Transaction](Transaction.md#abort-a-transaction)\n  * [Transaction Ordering](Transaction.md#transaction-ordering)\n  * [Direct Transactions](Transaction.md#direct-transactions)\n  * [Manual Transaction Handling](Transaction.md#manual-transaction-handling)\n  * [Accessing the Connection](Transaction.md#accessing-the-connection)\n* [Statement](Statement.md)\n  * [`execute()`](Statement.md#execute)\n    * [`tao::pq::internal::zsv`](Statement.md#taopqinternalzsv)\n  * [Positional Parameters](Statement.md#positional-parameters)\n  * [Multi-Query Commands](Statement.md#multi-query-commands)\n  * [Prepared Statements](Statement.md#prepared-statements)\n  * [Type Conversion](Statement.md#type-conversion)\n* [Parameter Type Conversion](Parameter-Type-Conversion.md)\n  * [NULL](Parameter-Type-Conversion.md#null)\n  * [Fundamental Types](Parameter-Type-Conversion.md#fundamental-types)\n  * [`std::optional< T >`](Parameter-Type-Conversion.md#stdoptional-t-)\n  * [`std::pair< T, U >`](Parameter-Type-Conversion.md#stdpair-t-u-)\n  * [`std::tuple< Ts... >`](Parameter-Type-Conversion.md#stdtuple-ts-)\n  * [Aggregates](Parameter-Type-Conversion.md#aggregates)\n  * [Custom Data Types](Parameter-Type-Conversion.md#custom-data-types)\n    * [`to_taopq()`](Parameter-Type-Conversion.md#to_taopq-)\n      * [Intrusive Placement](Parameter-Type-Conversion.md#intrusive-placement)\n      * [Non-Intrusive Placement](Parameter-Type-Conversion.md#non-intrusive-placement)\n    * [`tao::pq::parameter_traits< T >`](Parameter-Type-Conversion.md#taopqparameter_traits-t-)\n* [Result](Result.md)\n  * [Synopsis](Result.md#synopsis)\n  * [Non-Query Results](Result.md#non-query-results)\n  * [Query Results](Result.md#query-results)\n    * [Basics](Result.md#basics)\n    * [Row Access](Result.md#row-access)\n    * [Field Access](Result.md#field-access)\n    * [Fields](Result.md#fields)\n  * [Field Data Conversion](Result.md#field-data-conversion)\n  * [Row Data Conversion](Result.md#row-data-conversion)\n* [Result Type Conversion](Result-Type-Conversion.md)\n  * [Fundamental Types](Result-Type-Conversion.md#fundamental-types)\n  * [`std::optional< T >`](Result-Type-Conversion.md#stdoptional-t-)\n  * [`std::pair< T, U >`](Result-Type-Conversion.md#stdpair-t-u-)\n  * [`std::tuple< Ts... >`](Result-Type-Conversion.md#stdtuple-ts-)\n  * [Aggregates](Result-Type-Conversion.md#aggregates)\n  * [Custom Data Types](Result-Type-Conversion.md#custom-data-types)\n    * [`from_taopq()`](Result-Type-Conversion.md#from_taopq)\n      * [Intrusive Placement](Result-Type-Conversion.md#intrusive-placement)\n      * [Non-Intrusive Placement](Result-Type-Conversion.md#non-intrusive-placement)\n    * [`tao::pq::result_traits< T >`](Result-Type-Conversion.md#taopqresult_traits-t-)\n* [Aggregate Support](Aggregate-Support.md)\n  * [Status](Aggregate-Support.md#status)\n  * [Requirements](Aggregate-Support.md#requirements)\n  * [Registration](Aggregate-Support.md#registration)\n  * [Direct Result Conversion](Aggregate-Support.md#direct-result-conversion)\n  * [Example](Aggregate-Support.md#example)\n* [Error Handling](Error-Handling.md)\n  * [SQLSTATE](Error-Handling.md#sqlstate)\n    * [Class of Error](Error-Handling.md#class-of-error)\n    * [Specific Error Conditions](Error-Handling.md#specific-error-conditions)\n  * [Connection Errors](Error-Handling.md#connection-errors)\n  * [Other Exceptions](Error-Handling.md#other-exceptions)\n* [Binary Data](Binary-Data.md)\n  * [The `BYTEA` Data Type](Binary-Data.md#the-bytea-data-type)\n  * [C++ Binary Data](Binary-Data.md#c-binary-data)\n  * [Passing Binary Data](Binary-Data.md#passing-binary-data)\n  * [Receiving Binary Data](Binary-Data.md#receiving-binary-data)\n* [Bulk Transfer](Bulk-Transfer.md)\n  * [Synopsis](Bulk-Transfer.md#synopsis)\n* [Large Object](Large-Object.md)\n  * [Synopsis](Large-Object.md#synopsis)\n  * [Creating a Large Object](Large-Object.md#creating-a-large-object)\n  * [Removing a Large Object](Large-Object.md#removing-a-large-object)\n  * [Importing a Large Object](Large-Object.md#importing-a-large-object)\n  * [Exporting a Large Object](Large-Object.md#exporting-a-large-object)\n  * [Opening an Existing Large Object](Large-Object.md#opening-an-existing-large-object)\n  * [Writing Data to a Large Object](Large-Object.md#writing-data-to-a-large-object)\n  * [Reading Data from a Large Object](Large-Object.md#reading-data-from-a-large-object)\n  * [Seeking in a Large Object](Large-Object.md#seeking-in-a-large-object)\n  * [Obtaining the Seek Position of a Large Object](Large-Object.md#obtaining-the-seek-position-of-a-large-object)\n  * [Truncating a Large Object](Large-Object.md#truncating-a-large-object)\n* [Performance](Performance.md)\n"
  },
  {
    "path": "doc/Transaction.md",
    "content": "# Transaction\n\nBefore we continue with our own documentation, we'd like to once again point you to the excellent [PostgreSQL documentation➚](https://www.postgresql.org/docs/current/tutorial-transactions.html) on transactions.\nWe will assume that you are familiar with how transactions work on PostgreSQL and in general, so we will *not* repeat this here.\n\n## Synopsis\n\n```c++\nnamespace tao::pq\n{\n   namespace internal\n   {\n      class zsv;  // zero-terminated string view\n   }\n\n   class connection;\n   class result;\n\n   class transaction\n      : public std::enable_shared_from_this< transaction >\n   {\n   public:\n      // non-copyable, non-movable\n      transaction( const transaction& ) = delete;\n      transaction( transaction&& ) = delete;\n      void operator=( const transaction& ) = delete;\n      void operator=( transaction&& ) = delete;\n\n      virtual ~transaction() = default;\n\n      // create transactions\n      auto subtransaction()\n         -> std::shared_ptr< transaction >;\n\n      // asynchronous statement execution\n      template< typename... As >\n      void send( const internal::zsv statement, As&&... as );\n\n      // asynchronous result retrieval\n      auto get_result() -> result;\n\n      // synchronous statement execution\n      template< typename... As >\n      auto execute( const internal::zsv statement, As&&... as )\n      {\n         send( statement, std::forward< As >( as )... );\n         return get_result();\n      }\n\n      // finalize\n      void commit();\n      void rollback();\n\n      // access connection\n      auto connection() const noexcept\n         -> const std::shared_ptr< pq::connection >&;\n   };\n}\n```\n\n:point_up: Note that `tao::pq::internal::zsv` is explained in the [Statement](Statement.md) chapter.\n\n## Creating Transactions\n\nIn taoPQ, you create a top-level transaction from a connection, the methods available to do so are described in the [Connection](Connection.md) chapter.\nIn short, you create a normal transaction with the connection's `transaction()`-method or a \"direct transaction\" with the connection's `direct()`-method.\nBoth return a shared pointer to a `tao::pq::transaction`-derived object.\n\nFrom any transaction, you can create a subtransaction by calling the `subtransaction()`-method.\n\n```c++\nauto tao::pq::transaction::subtransaction()\n   -> std::shared_ptr< tao::pq::transaction >;\n```\n\nIt returns just another `tao::pq::transaction`-derived object from which you may create further, nested subtransactions if needed.\n\nAll transactions then offer the above, unified interface.\n\n## Statement Execution\n\nOn all transactions you can execute SQL statements.\nIf you execute a statement on a connection object directly, is creates an implicit direct transaction and forwards the execution to that temporary transaction.\nThe actual statement execution, i.e. the `execute()`-method, is described in the [Statement](Statement.md) chapter.\n\n## Terminate Transaction\n\nTransaction can be terminated in one of two ways.\n\n### Commit a Transaction\n\nIn order to commit a transaction you call the `commit()`-method.\n\n```c++\nvoid tao::pq::transaction::commit();\n```\n\nAll changes made by the transaction become visible to others and are guaranteed to be durable if a crash occurs.\n\n### Abort a Transaction\n\nIn order to abort a transaction you call the `rollback()`-method.\n\n```c++\nvoid tao::pq::transaction::rollback();\n```\n\nRolls back the current transaction and causes all the updates made by the transaction to be discarded.\n\n## Transaction Ordering\n\nAny transactions created via taoPQ is registered in the connection object as the currently active transaction.\nAt any given time, a connection can only have a single active transaction.\nIf you attempt to use a transaction object in the wrong order, taoPQ will notice and throw an appropriate `std::logic_error` exception.\n\n:point_up: Note that the correct order depends on the *logical* lifetime of transactions.\nThe logical lifetime of a transactions ends when you explicitly call either the `commit()`- or the `rollback()`-method, or if the object's lifetime ends.\nThe destructor will automatically perform a call to the `rollback()`-method if the lifetime was not ended explicitly.\nThis comes in handy when exceptions are thrown and the destructor call happens due to the associated stack unwinding.\n\n## Direct Transactions\n\nWithout an active transaction PostgreSQL works in [\"autocommit\"➚](https://www.postgresql.org/docs/current/sql-begin.html) mode.\nThis means that each statement is executed in its own internal transaction and a commit is implicitly performed at the end of the statement (if execution was successful, otherwise a rollback is done).\nA \"direct transaction\" represents this concept in taoPQ.\n\nA direct transaction is special, as the `commit()`- and `rollback()`-methods are available, but normally not needed.\nSpecifically, you don't need to call the `commit()`-method in order to make the changes you made permanent.\nHowever, calling the `commit()`-method ends the logical lifetime of the transaction and the transaction deregisters itself from the connection.\nIn generic code you might receive a transaction from somewhere else and you might call the `commit()`-method regardless of whether the underlying transaction is a direct transaction or a normal transaction.\n\nIn case you need to know whether a given transaction object is a direct transaction or not, you can call the `is_direct()`-method.\n\n```c++\nbool tao::pq::transaction::is_direct() const noexcept;\n```\n\nOpening a subtransaction from a direct connection is possible and simply starts a normal transaction on the connection object.\n\n## Manual Transaction Handling\n\nYou can manually begin, commit, or rollback transactions by executing [`BEGIN`➚](https://www.postgresql.org/docs/current/sql-begin.html), [`COMMIT`➚](https://www.postgresql.org/docs/current/sql-commit.html), or [`ROLLBACK`➚](https://www.postgresql.org/docs/current/sql-rollback.html) statements directly via the `execute()`-method.\nLikewise, you can manually create, commit, or rollback subtransactions by executing [`SAVEPOINT`➚](https://www.postgresql.org/docs/current/sql-savepoint.html), [`RELEASE SAVEPOINT`➚](https://www.postgresql.org/docs/current/sql-release-savepoint.html), or [`ROLLBACK TO SAVEPOINT`➚](https://www.postgresql.org/docs/current/sql-rollback-to.html) statements directly via the `execute()`-method.\n\n:point_up: We strongly advise against manual transaction handling, as it will not be tracked by taoPQ and might confuse our library's transaction ordering framework.\nWe advise to use the methods offered by taoPQ instead of manually handling transactions.\n\n## Accessing the Connection\n\nIf you need to access the connection that a transaction is bound to, you can call the `connection()`-method.\n\n```c++\nauto tao::pq::transaction::connection() const noexcept\n   -> const std::shared_ptr< tao::pq::connection >&;\n```\n\n:point_up: Note that the shared pointer will be empty if the logical lifetime of the transaction ended.\n\n---\n\nThis document is part of [taoPQ](https://github.com/taocpp/taopq).\n\nCopyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch<br>\nDistributed under the Boost Software License, Version 1.0<br>\nSee accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt\n"
  },
  {
    "path": "example/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\n\nadd_subdirectory(get_version)\n\nset(EXAMPLE_TARGETS\n  taopq_example_get_version\n)\n\nadd_custom_target(examples\n  COMMENT \"Run all examples\"\n  DEPENDS ${EXAMPLE_TARGETS}\n)\n\nforeach(tgt IN LISTS EXAMPLE_TARGETS)\n  add_custom_command(TARGET examples POST_BUILD\n    COMMAND \"$<TARGET_FILE:${tgt}>\"\n    WORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}\"\n    COMMENT \"Running ${tgt}\"\n    VERBATIM\n  )\nendforeach()"
  },
  {
    "path": "example/get_version/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\nproject(taopq_example_get_version CXX)\n\nadd_executable(${PROJECT_NAME} main.cpp)\ntarget_link_libraries(${PROJECT_NAME} PRIVATE taocpp::taopq)\ntarget_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20 c_std_11)\n"
  },
  {
    "path": "example/get_version/main.cpp",
    "content": "#include <cstdlib>\n#include <iostream>\n#include <string>\n#include <vector>\n\n#include <tao/pq.hpp>\n\nstatic std::string get_env( std::string_view name )\n{\n#if defined( _MSC_VER )\n   std::size_t required = 1024;\n   std::vector< char > buffer( required );\n   const auto err = ::getenv_s( &required, buffer.data(), buffer.size(), std::string( name ).c_str() );\n   if( err != 0 ) {\n      return {};\n   }\n   return std::string( buffer.data() );\n#else\n   if( const char* v = std::getenv( std::string( name ).c_str() ) ) {\n      return std::string( v );\n   }\n   return {};\n#endif\n}\n\nint main()\n{\n   const auto database = get_env( \"PGDATABASE\" );\n   const auto connection = tao::pq::connection::create( database );\n   const auto result = connection->execute( \"SELECT version()\" );\n   std::cout << \"PostgreSQL version: \" << result.as< std::string >() << std::endl;\n   return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "include/tao/pq/access_mode.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_ACCESS_MODE_HPP\n#define TAO_PQ_ACCESS_MODE_HPP\n\n#include <cstdint>\n#include <string_view>\n\n#include <tao/pq/internal/format_as.hpp>\n\nnamespace tao::pq\n{\n   enum class access_mode : std::uint8_t\n   {\n      default_access_mode,\n      read_write,\n      read_only\n   };\n\n   [[nodiscard]] constexpr auto taopq_format_as( const access_mode am ) noexcept -> std::string_view\n   {\n      switch( am ) {\n         case access_mode::default_access_mode:\n            return \"default_access_mode\";\n\n         case access_mode::read_write:\n            return \"read_write\";\n\n         case access_mode::read_only:\n            return \"read_only\";\n\n         default:\n            return \"<unknown>\";\n      }\n   }\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/binary.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_BINARY_HPP\n#define TAO_PQ_BINARY_HPP\n\n#include <cstddef>\n#include <span>\n#include <vector>\n\nnamespace tao::pq\n{\n   using binary = std::vector< std::byte >;\n   using binary_view = std::span< const std::byte >;\n\n   [[nodiscard]] auto to_binary_view( const auto* data, const std::size_t size ) noexcept -> binary_view\n      requires( sizeof( *data ) == 1 )\n   {\n      return { reinterpret_cast< const std::byte* >( data ), size };\n   }\n\n   [[nodiscard]] auto to_binary_view( const auto& value ) noexcept -> binary_view\n   {\n      return pq::to_binary_view( std::data( value ), std::size( value ) );\n   }\n\n   [[nodiscard]] auto to_binary( const auto* data, const std::size_t size ) -> binary\n      requires( sizeof( *data ) == 1 )\n   {\n      const auto* ptr = reinterpret_cast< const std::byte* >( data );\n      return { ptr, ptr + size };\n   }\n\n   [[nodiscard]] auto to_binary( const auto& value ) -> binary\n   {\n      return pq::to_binary( std::data( value ), std::size( value ) );\n   }\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/bind.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_BIND_HPP\n#define TAO_PQ_BIND_HPP\n\nnamespace tao::pq\n{\n   template< typename >\n   struct bind;\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/connection.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_CONNECTION_HPP\n#define TAO_PQ_CONNECTION_HPP\n\n#include <chrono>\n#include <cstddef>\n#include <functional>\n#include <map>\n#include <memory>\n#include <optional>\n#include <set>\n#include <string>\n#include <string_view>\n#include <utility>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/access_mode.hpp>\n#include <tao/pq/connection_status.hpp>\n#include <tao/pq/internal/poll.hpp>\n#include <tao/pq/internal/zsv.hpp>\n#include <tao/pq/isolation_level.hpp>\n#include <tao/pq/log.hpp>\n#include <tao/pq/notification.hpp>\n#include <tao/pq/parameter.hpp>\n#include <tao/pq/pipeline_status.hpp>\n#include <tao/pq/poll.hpp>\n#include <tao/pq/transaction.hpp>\n#include <tao/pq/transaction_base.hpp>\n#include <tao/pq/transaction_status.hpp>\n\nnamespace tao::pq\n{\n   class connection_pool;\n   class pipeline;\n   class table_reader;\n   class table_writer;\n\n   namespace internal\n   {\n      class top_level_transaction;\n      class top_level_subtransaction;\n      class nested_subtransaction;\n\n   }  // namespace internal\n\n   class connection final\n      : public std::enable_shared_from_this< connection >\n   {\n   private:\n      friend class connection_pool;\n      friend class table_reader;\n      friend class table_writer;\n      friend class transaction;\n      friend class transaction_base;\n\n      friend class internal::top_level_transaction;\n      friend class internal::top_level_subtransaction;\n      friend class internal::nested_subtransaction;\n\n      std::unique_ptr< PGconn, decltype( &PQfinish ) > m_pgconn;\n      transaction_base* m_current_transaction;\n      std::optional< std::chrono::milliseconds > m_timeout;\n      std::set< std::string, std::less<> > m_prepared_statements;\n      std::function< poll::callback > m_poll;\n      std::function< void( const notification& ) > m_notification_handler;\n      std::map< std::string, std::function< void( const char* ) >, std::less<> > m_notification_handlers;\n      std::shared_ptr< log > m_log;\n\n      [[nodiscard]] auto escape_identifier( const std::string_view identifier ) const -> std::unique_ptr< char, decltype( &PQfreemem ) >;\n\n      [[nodiscard]] auto attempt_rollback() const noexcept -> bool;\n\n      static void check_prepared_name( const std::string_view name );\n\n      void send_params( const char* statement,\n                        const int n_params,\n                        const Oid types[],\n                        const char* const values[],\n                        const int lengths[],\n                        const int formats[] );\n\n      [[nodiscard]] auto timeout_end( const std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now() ) const noexcept -> std::chrono::steady_clock::time_point\n      {\n         return m_timeout ? ( start + *m_timeout ) : start;\n      }\n\n      void wait( const bool wait_for_write, const std::chrono::steady_clock::time_point end );\n      void cancel();\n\n      [[nodiscard]] auto get_result( const std::chrono::steady_clock::time_point end ) -> std::unique_ptr< PGresult, decltype( &PQclear ) >;\n      [[nodiscard]] auto get_fatal_error( const std::chrono::steady_clock::time_point end ) -> std::unique_ptr< PGresult, decltype( &PQclear ) >;\n      void consume_empty_result( const std::chrono::steady_clock::time_point end );\n\n      [[nodiscard]] auto get_copy_data( char*& buffer, const std::chrono::steady_clock::time_point end ) -> std::size_t;\n      [[nodiscard]] auto get_copy_data( char*& buffer ) -> std::size_t;\n\n      void put_copy_data( const char* buffer, const std::size_t size );\n      void put_copy_end( const char* error_message = nullptr );\n\n      void clear_copy_data( const std::chrono::steady_clock::time_point end );\n\n      // pass-key idiom\n      class private_key final\n      {\n         private_key() = default;\n         friend class connection;\n         friend class connection_pool;\n      };\n\n   public:\n      connection( const private_key /*unused*/, const std::string& connection_info );\n\n      connection( const connection& ) = delete;\n      connection( connection&& ) = delete;\n      void operator=( const connection& ) = delete;\n      void operator=( connection&& ) = delete;\n\n      ~connection() = default;\n\n      [[nodiscard]] static auto create( const std::string& connection_info ) -> std::shared_ptr< connection >;\n\n      [[nodiscard]] auto error_message() const -> const char*;\n\n      [[nodiscard]] auto poll_callback() const noexcept -> decltype( auto )\n      {\n         return m_poll;\n      }\n\n      void set_poll_callback( std::function< poll::callback > poll_cb ) noexcept\n      {\n         m_poll = std::move( poll_cb );\n      }\n\n      void reset_poll_callback()\n      {\n         m_poll = internal::poll;\n      }\n\n      [[nodiscard]] auto notification_handler() const noexcept -> decltype( auto )\n      {\n         return m_notification_handler;\n      }\n\n      void set_notification_handler( std::function< void( const notification& ) > handler ) noexcept\n      {\n         m_notification_handler = std::move( handler );\n      }\n\n      void reset_notification_handler() noexcept\n      {\n         m_notification_handler = nullptr;\n      }\n\n      [[nodiscard]] auto notification_handler( const std::string_view channel ) const -> std::function< void( const char* payload ) >;\n\n      void set_notification_handler( const std::string_view channel, const std::function< void( const char* payload ) >& handler );\n\n      void reset_notification_handler( const std::string_view channel ) noexcept;\n\n      [[nodiscard]] auto log_handler() const noexcept -> decltype( auto )\n      {\n         return m_log;\n      }\n\n      void set_log_handler( const std::shared_ptr< pq::log >& log ) noexcept\n      {\n         m_log = log;\n      }\n\n      void reset_log_handler() noexcept\n      {\n         m_log = nullptr;\n      }\n\n      [[nodiscard]] auto status() const noexcept -> connection_status;\n      [[nodiscard]] auto transaction_status() const noexcept -> pq::transaction_status;\n\n      [[nodiscard]] auto pipeline_status() const noexcept -> pq::pipeline_status;\n      void enter_pipeline_mode();\n      void exit_pipeline_mode();\n\n      void pipeline_sync();\n\n      [[nodiscard]] auto is_open() const noexcept -> bool\n      {\n         return status() == connection_status::ok;\n      }\n\n      [[nodiscard]] auto is_idle() const noexcept -> bool\n      {\n         return transaction_status() == transaction_status::idle;\n      }\n\n      [[nodiscard]] auto is_busy() const noexcept -> bool;\n\n      [[nodiscard]] auto flush() -> bool;\n\n      void consume_input();\n\n      [[nodiscard]] auto direct() -> std::shared_ptr< pq::transaction >;\n\n      [[nodiscard]] auto transaction() -> std::shared_ptr< pq::transaction >;\n      [[nodiscard]] auto transaction( const access_mode am, const isolation_level il = isolation_level::default_isolation_level ) -> std::shared_ptr< pq::transaction >;\n      [[nodiscard]] auto transaction( const isolation_level il, const access_mode am = access_mode::default_access_mode ) -> std::shared_ptr< pq::transaction >;\n\n      [[nodiscard]] auto pipeline() -> std::shared_ptr< pq::pipeline >;\n\n      void prepare( std::string name, const internal::zsv statement );\n      void deallocate( const std::string_view name );\n\n      template< parameter_type... As >\n      auto execute( const internal::zsv statement, As&&... as )\n      {\n         return direct()->execute( statement, std::forward< As >( as )... );\n      }\n\n      void listen( const std::string_view channel );\n      void listen( const std::string_view channel, const std::function< void( const char* payload ) >& handler );\n      void unlisten( const std::string_view channel );\n\n      void notify( const std::string_view channel );\n      void notify( const std::string_view channel, const std::string_view payload );\n\n      void handle_notifications();\n      void get_notifications();\n\n      [[nodiscard]] auto socket() const -> int;\n\n      [[nodiscard]] auto timeout() const noexcept -> decltype( auto )\n      {\n         return m_timeout;\n      }\n\n      void set_timeout( const std::chrono::milliseconds timeout ) noexcept\n      {\n         m_timeout = timeout;\n      }\n\n      void reset_timeout() noexcept\n      {\n         m_timeout = std::nullopt;\n      }\n\n      [[nodiscard]] auto password( const internal::zsv passwd, const internal::zsv user, const internal::zsv algorithm = \"scram-sha-256\" ) -> std::string;\n\n      [[nodiscard]] auto underlying_raw_ptr() noexcept -> PGconn*\n      {\n         return m_pgconn.get();\n      }\n\n      [[nodiscard]] auto underlying_raw_ptr() const noexcept -> const PGconn*\n      {\n         return m_pgconn.get();\n      }\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/connection_pool.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_CONNECTION_POOL_HPP\n#define TAO_PQ_CONNECTION_POOL_HPP\n\n#include <chrono>\n#include <functional>\n#include <memory>\n#include <optional>\n#include <string>\n#include <string_view>\n#include <utility>\n\n#include <tao/pq/connection.hpp>\n#include <tao/pq/internal/poll.hpp>\n#include <tao/pq/internal/pool.hpp>\n#include <tao/pq/internal/zsv.hpp>\n#include <tao/pq/parameter.hpp>\n#include <tao/pq/poll.hpp>\n#include <tao/pq/result.hpp>\n\nnamespace tao::pq\n{\n   class connection_pool\n      : public internal::pool< pq::connection >\n   {\n   private:\n      const std::string m_connection_info;\n      std::optional< std::chrono::milliseconds > m_timeout;\n      std::function< poll::callback > m_poll;\n\n   protected:\n      [[nodiscard]] auto v_create() const -> std::unique_ptr< pq::connection > override;\n\n      [[nodiscard]] auto v_is_valid( pq::connection& c ) const noexcept -> bool override\n      {\n         return c.is_idle();\n      }\n\n   private:\n      // pass-key idiom\n      class private_key final\n      {\n         private_key() = default;\n         friend class connection_pool;\n      };\n\n   public:\n      connection_pool( const private_key /*unused*/, const std::string_view connection_info );\n\n      void get() const = delete;\n\n      template< typename T = connection_pool >\n      [[nodiscard]] static auto create( const std::string_view connection_info ) -> std::shared_ptr< T >\n      {\n         return std::make_shared< T >( private_key(), connection_info );\n      }\n\n      [[nodiscard]] auto timeout() const noexcept -> decltype( auto )\n      {\n         return m_timeout;\n      }\n\n      void set_timeout( const std::chrono::milliseconds timeout ) noexcept\n      {\n         m_timeout = timeout;\n      }\n\n      void reset_timeout() noexcept\n      {\n         m_timeout = std::nullopt;\n      }\n\n      [[nodiscard]] auto poll_callback() const noexcept -> decltype( auto )\n      {\n         return m_poll;\n      }\n\n      void set_poll_callback( std::function< poll::callback > poll_cb ) noexcept\n      {\n         m_poll = std::move( poll_cb );\n      }\n\n      void reset_poll_callback()\n      {\n         m_poll = internal::poll;\n      }\n\n      [[nodiscard]] auto connection() -> std::shared_ptr< pq::connection >;\n\n      template< parameter_type... As >\n      auto execute( const internal::zsv statement, As&&... as )\n      {\n         return connection()->direct()->execute( statement, std::forward< As >( as )... );\n      }\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/connection_status.hpp",
    "content": "// Copyright (c) 2022-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_CONNECTION_STATUS_HPP\n#define TAO_PQ_CONNECTION_STATUS_HPP\n\n#include <cstdint>\n#include <string_view>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/internal/format_as.hpp>\n\nnamespace tao::pq\n{\n   enum class connection_status : std::uint8_t\n   {\n      ok = CONNECTION_OK,\n      bad = CONNECTION_BAD\n   };\n\n   [[nodiscard]] constexpr auto taopq_format_as( const connection_status cs ) noexcept -> std::string_view\n   {\n      switch( cs ) {\n         case connection_status::ok:\n            return \"ok\";\n\n         case connection_status::bad:\n            return \"bad\";\n\n         default:\n            return \"<unknown>\";\n      }\n   }\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/exception.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_EXCEPTION_HPP\n#define TAO_PQ_EXCEPTION_HPP\n\n#include <stdexcept>\n#include <string>\n#include <string_view>\n\n#include <libpq-fe.h>\n\nnamespace tao::pq\n{\n   struct error\n      : std::runtime_error\n   {\n      using std::runtime_error::runtime_error;\n   };\n\n   struct timeout_reached\n      : error\n   {\n      using error::error;\n   };\n\n   struct network_error\n      : error\n   {\n      using error::error;\n   };\n\n   // https://www.postgresql.org/docs/current/errcodes-appendix.html\n   struct sql_error\n      : error\n   {\n      std::string sqlstate;\n\n      sql_error( const char* what, const std::string_view in_sqlstate );\n   };\n\n   // when a condition name from PostgreSQL is ambiguous,\n   // we qualify the error class via a template parameter\n   template< typename >\n   struct string_data_right_truncation;\n\n   template< typename >\n   struct modifying_sql_data_not_permitted;\n\n   template< typename >\n   struct prohibited_sql_statement_attempted;\n\n   template< typename >\n   struct reading_sql_data_not_permitted;\n\n   struct success  // 00xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct warning  // 01xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct null_value_eliminated_in_set_function  // 01003\n      : warning\n   {\n      using warning::warning;\n   };\n\n   template<>\n   struct string_data_right_truncation< warning >  // 01004\n      : warning\n   {\n      using warning::warning;\n   };\n\n   struct privilege_not_revoked  // 01006\n      : warning\n   {\n      using warning::warning;\n   };\n\n   struct privilege_not_granted  // 01007\n      : warning\n   {\n      using warning::warning;\n   };\n\n   struct implicit_zero_bit_padding  // 01008\n      : warning\n   {\n      using warning::warning;\n   };\n\n   struct dynamic_result_sets_returned  // 0100C\n      : warning\n   {\n      using warning::warning;\n   };\n\n   struct deprecated_feature  // 01P01\n      : warning\n   {\n      using warning::warning;\n   };\n\n   struct no_data  // 02xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct no_additional_dynamic_result_sets_returned  // 02001\n      : no_data\n   {\n      using no_data::no_data;\n   };\n\n   struct sql_statement_not_yet_complete  // 03xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct connection_error  // 08xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n\n      explicit connection_error( const char* what );\n   };\n\n   struct sqlclient_unable_to_establish_sqlconnection  // 08001\n      : connection_error\n   {\n      using connection_error::connection_error;\n   };\n\n   struct connection_does_not_exist  // 08003\n      : connection_error\n   {\n      using connection_error::connection_error;\n   };\n\n   struct sqlserver_rejected_establishment_of_sqlconnection  // 08004\n      : connection_error\n   {\n      using connection_error::connection_error;\n   };\n\n   struct connection_failure  // 08006\n      : connection_error\n   {\n      using connection_error::connection_error;\n   };\n\n   struct transaction_resolution_unknown  // 08007\n      : connection_error\n   {\n      using connection_error::connection_error;\n   };\n\n   struct protocol_violation  // 08P01\n      : connection_error\n   {\n      using connection_error::connection_error;\n   };\n\n   struct triggered_action_exception  // 09xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct feature_not_supported  // 0Axxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct invalid_transaction_initiation  // 0Bxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct locator_exception  // 0Fxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct invalid_locator_specification  // 0F001\n      : locator_exception\n   {\n      using locator_exception::locator_exception;\n   };\n\n   struct invalid_grantor  // 0Lxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct invalid_grant_operation  // 0LP01\n      : invalid_grantor\n   {\n      using invalid_grantor::invalid_grantor;\n   };\n\n   struct invalid_role_specification  // 0Pxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct diagnostics_exception  // 0Zxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct stacked_diagnostics_accessed_without_active_handler  // 0Z002\n      : diagnostics_exception\n   {\n      using diagnostics_exception::diagnostics_exception;\n   };\n\n   struct case_not_found  // 20xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct cardinality_violation  // 21xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct data_exception  // 22xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct array_subscript_error  // 2202E\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct character_not_in_repertoire  // 22021\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct datetime_field_overflow  // 22008\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct division_by_zero  // 22012\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct error_in_assignment  // 22005\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct escape_character_conflict  // 2200B\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct indicator_overflow  // 22022\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct interval_field_overflow  // 22015\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_argument_for_logarithm  // 2201E\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_argument_for_ntile_function  // 22014\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_argument_for_nth_value_function  // 22016\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_argument_for_power_function  // 2201F\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_argument_for_width_bucket_function  // 2201G\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_character_value_for_cast  // 22018\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_datetime_format  // 22007\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_escape_character  // 22019\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_escape_octet  // 2200D\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_escape_sequence  // 22025\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct nonstandard_use_of_escape_character  // 22P06\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_indicator_parameter_value  // 22010\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_parameter_value  // 22023\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_preceding_or_following_size  // 22013\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_regular_expression  // 2201B\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_row_count_in_limit_clause  // 2201W\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_row_count_in_result_offset_clause  // 2201X\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_tablesample_argument  // 2202H\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_tablesample_repeat  // 2202G\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_time_zone_displacement_value  // 22009\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_use_of_escape_character  // 2200C\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct most_specific_type_mismatch  // 2200G\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct null_value_not_allowed  // 22004\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct null_value_no_indicator_parameter  // 22002\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct numeric_value_out_of_range  // 22003\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct sequence_generator_limit_exceeded  // 2200H\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct string_data_length_mismatch  // 22026\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   template<>\n   struct string_data_right_truncation< data_exception >  // 22001\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct substring_error  // 22011\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct trim_error  // 22027\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct unterminated_c_string  // 22024\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct zero_length_character_string  // 2200F\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct floating_point_exception  // 22P01\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_text_representation  // 22P02\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_binary_representation  // 22P03\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct bad_copy_file_format  // 22P04\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct untranslatable_character  // 22P05\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct not_an_xml_document  // 2200L\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_xml_document  // 2200M\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_xml_content  // 2200N\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_xml_comment  // 2200S\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_xml_processing_instruction  // 2200T\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct duplicate_json_object_key_value  // 22030\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_argument_for_sql_json_datetime_function  // 22031\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_json_text  // 22032\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct invalid_sql_json_subscript  // 22033\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct more_than_one_sql_json_item  // 22034\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct no_sql_json_item  // 22035\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct non_numeric_sql_json_item  // 22036\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct non_unique_keys_in_a_json_object  // 22037\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct singleton_sql_json_item_required  // 22038\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct sql_json_array_not_found  // 22039\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct sql_json_member_not_found  // 2203A\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct sql_json_number_not_found  // 2203B\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct sql_json_object_not_found  // 2203C\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct too_many_json_array_elements  // 2203D\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct too_many_json_object_members  // 2203E\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct sql_json_scalar_required  // 2203F\n      : data_exception\n   {\n      using data_exception::data_exception;\n   };\n\n   struct integrity_constraint_violation  // 23xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct restrict_violation  // 23001\n      : integrity_constraint_violation\n   {\n      using integrity_constraint_violation::integrity_constraint_violation;\n   };\n\n   struct not_null_violation  // 23502\n      : integrity_constraint_violation\n   {\n      using integrity_constraint_violation::integrity_constraint_violation;\n   };\n\n   struct foreign_key_violation  // 23503\n      : integrity_constraint_violation\n   {\n      using integrity_constraint_violation::integrity_constraint_violation;\n   };\n\n   struct unique_violation  // 23505\n      : integrity_constraint_violation\n   {\n      using integrity_constraint_violation::integrity_constraint_violation;\n   };\n\n   struct check_violation  // 23514\n      : integrity_constraint_violation\n   {\n      using integrity_constraint_violation::integrity_constraint_violation;\n   };\n\n   struct exclusion_violation  // 23P01\n      : integrity_constraint_violation\n   {\n      using integrity_constraint_violation::integrity_constraint_violation;\n   };\n\n   struct invalid_cursor_state  // 24xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct invalid_transaction_state  // 25xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct active_sql_transaction  // 25001\n      : invalid_transaction_state\n   {\n      using invalid_transaction_state::invalid_transaction_state;\n   };\n\n   struct branch_transaction_already_active  // 25002\n      : invalid_transaction_state\n   {\n      using invalid_transaction_state::invalid_transaction_state;\n   };\n\n   struct held_cursor_requires_same_isolation_level  // 25008\n      : invalid_transaction_state\n   {\n      using invalid_transaction_state::invalid_transaction_state;\n   };\n\n   struct inappropriate_access_mode_for_branch_transaction  // 25003\n      : invalid_transaction_state\n   {\n      using invalid_transaction_state::invalid_transaction_state;\n   };\n\n   struct inappropriate_isolation_level_for_branch_transaction  // 25004\n      : invalid_transaction_state\n   {\n      using invalid_transaction_state::invalid_transaction_state;\n   };\n\n   struct no_active_sql_transaction_for_branch_transaction  // 25005\n      : invalid_transaction_state\n   {\n      using invalid_transaction_state::invalid_transaction_state;\n   };\n\n   struct read_only_sql_transaction  // 25006\n      : invalid_transaction_state\n   {\n      using invalid_transaction_state::invalid_transaction_state;\n   };\n\n   struct schema_and_data_statement_mixing_not_supported  // 25007\n      : invalid_transaction_state\n   {\n      using invalid_transaction_state::invalid_transaction_state;\n   };\n\n   struct no_active_sql_transaction  // 25P01\n      : invalid_transaction_state\n   {\n      using invalid_transaction_state::invalid_transaction_state;\n   };\n\n   struct in_failed_sql_transaction  // 25P02\n      : invalid_transaction_state\n   {\n      using invalid_transaction_state::invalid_transaction_state;\n   };\n\n   struct idle_in_transaction_session_timeout  // 25P03\n      : invalid_transaction_state\n   {\n      using invalid_transaction_state::invalid_transaction_state;\n   };\n\n   struct invalid_sql_statement_name  // 26xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct triggered_data_change_violation  // 27xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct invalid_authorization_specification  // 28xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct invalid_password  // 28P01\n      : invalid_authorization_specification\n   {\n      using invalid_authorization_specification::invalid_authorization_specification;\n   };\n\n   struct dependent_privilege_descriptors_still_exist  // 2Bxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct dependent_objects_still_exist  // 2BP01\n      : dependent_privilege_descriptors_still_exist\n   {\n      using dependent_privilege_descriptors_still_exist::dependent_privilege_descriptors_still_exist;\n   };\n\n   struct invalid_transaction_termination  // 2Dxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct sql_routine_exception  // 2Fxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   template<>\n   struct modifying_sql_data_not_permitted< sql_routine_exception >  // 2F002\n      : sql_routine_exception\n   {\n      using sql_routine_exception::sql_routine_exception;\n   };\n\n   template<>\n   struct prohibited_sql_statement_attempted< sql_routine_exception >  // 2F003\n      : sql_routine_exception\n   {\n      using sql_routine_exception::sql_routine_exception;\n   };\n\n   template<>\n   struct reading_sql_data_not_permitted< sql_routine_exception >  // 2F004\n      : sql_routine_exception\n   {\n      using sql_routine_exception::sql_routine_exception;\n   };\n\n   struct function_executed_no_return_statement  // 2F005\n      : sql_routine_exception\n   {\n      using sql_routine_exception::sql_routine_exception;\n   };\n\n   struct invalid_cursor_name  // 34xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct external_routine_exception  // 38xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct containing_sql_not_permitted  // 38001\n      : external_routine_exception\n   {\n      using external_routine_exception::external_routine_exception;\n   };\n\n   template<>\n   struct modifying_sql_data_not_permitted< external_routine_exception >  // 38002\n      : external_routine_exception\n   {\n      using external_routine_exception::external_routine_exception;\n   };\n\n   template<>\n   struct prohibited_sql_statement_attempted< external_routine_exception >  // 38003\n      : external_routine_exception\n   {\n      using external_routine_exception::external_routine_exception;\n   };\n\n   template<>\n   struct reading_sql_data_not_permitted< external_routine_exception >  // 38004\n      : external_routine_exception\n   {\n      using external_routine_exception::external_routine_exception;\n   };\n\n   struct external_routine_invocation_exception  // 39xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct invalid_sqlstate_returned  // 39001\n      : external_routine_invocation_exception\n   {\n      using external_routine_invocation_exception::external_routine_invocation_exception;\n   };\n\n   struct external_null_value_not_allowed  // 39004\n      : external_routine_invocation_exception\n   {\n      using external_routine_invocation_exception::external_routine_invocation_exception;\n   };\n\n   struct trigger_protocol_violated  // 39P01\n      : external_routine_invocation_exception\n   {\n      using external_routine_invocation_exception::external_routine_invocation_exception;\n   };\n\n   struct srf_protocol_violated  // 39P02\n      : external_routine_invocation_exception\n   {\n      using external_routine_invocation_exception::external_routine_invocation_exception;\n   };\n\n   struct event_trigger_protocol_violated  // 39P03\n      : external_routine_invocation_exception\n   {\n      using external_routine_invocation_exception::external_routine_invocation_exception;\n   };\n\n   struct savepoint_exception  // 3Bxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct invalid_savepoint_specification  // 3B001\n      : savepoint_exception\n   {\n      using savepoint_exception::savepoint_exception;\n   };\n\n   struct invalid_catalog_name  // 3Dxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct invalid_schema_name  // 3Fxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct transaction_rollback  // 40xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct serialization_failure  // 40001\n      : transaction_rollback\n   {\n      using transaction_rollback::transaction_rollback;\n   };\n\n   struct transaction_integrity_constraint_violation  // 40002\n      : transaction_rollback\n   {\n      using transaction_rollback::transaction_rollback;\n   };\n\n   struct statement_completion_unknown  // 40003\n      : transaction_rollback\n   {\n      using transaction_rollback::transaction_rollback;\n   };\n\n   struct deadlock_detected  // 40P01\n      : transaction_rollback\n   {\n      using transaction_rollback::transaction_rollback;\n   };\n\n   struct syntax_error_or_access_rule_violation  // 42xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct insufficient_privilege  // 42501\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct syntax_error  // 42601\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_name  // 42602\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_column_definition  // 42611\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct name_too_long  // 42622\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct duplicate_column  // 42701\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct ambiguous_column  // 42702\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct undefined_column  // 42703\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct undefined_object  // 42704\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct duplicate_object  // 42710\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct duplicate_alias  // 42712\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct duplicate_function  // 42723\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct ambiguous_function  // 42725\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct grouping_error  // 42803\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct datatype_mismatch  // 42804\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct wrong_object_type  // 42809\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_foreign_key  // 42830\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct cannot_coerce  // 42846\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct undefined_function  // 42883\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct generated_always  // 428C9\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct reserved_name  // 42939\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct undefined_table  // 42P01\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct undefined_parameter  // 42P02\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct duplicate_cursor  // 42P03\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct duplicate_database  // 42P04\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct duplicate_prepared_statement  // 42P05\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct duplicate_schema  // 42P06\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct duplicate_table  // 42P07\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct ambiguous_parameter  // 42P08\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct ambiguous_alias  // 42P09\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_column_reference  // 42P10\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_cursor_definition  // 42P11\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_database_definition  // 42P12\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_function_definition  // 42P13\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_prepared_statement_definition  // 42P14\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_schema_definition  // 42P15\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_table_definition  // 42P16\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_object_definition  // 42P17\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct indeterminate_datatype  // 42P18\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct invalid_recursion  // 42P19\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct windowing_error  // 42P20\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct collation_mismatch  // 42P21\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct indeterminate_collation  // 42P22\n      : syntax_error_or_access_rule_violation\n   {\n      using syntax_error_or_access_rule_violation::syntax_error_or_access_rule_violation;\n   };\n\n   struct with_check_option_violation  // 44xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct insufficient_resources  // 53xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct disk_full  // 53100\n      : insufficient_resources\n   {\n      using insufficient_resources::insufficient_resources;\n   };\n\n   struct out_of_memory  // 53200\n      : insufficient_resources\n   {\n      using insufficient_resources::insufficient_resources;\n   };\n\n   struct too_many_connections  // 53300\n      : insufficient_resources\n   {\n      using insufficient_resources::insufficient_resources;\n   };\n\n   struct configuration_limit_exceeded  // 53400\n      : insufficient_resources\n   {\n      using insufficient_resources::insufficient_resources;\n   };\n\n   struct program_limit_exceeded  // 54xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct statement_too_complex  // 54001\n      : program_limit_exceeded\n   {\n      using program_limit_exceeded::program_limit_exceeded;\n   };\n\n   struct too_many_columns  // 54011\n      : program_limit_exceeded\n   {\n      using program_limit_exceeded::program_limit_exceeded;\n   };\n\n   struct too_many_arguments  // 54023\n      : program_limit_exceeded\n   {\n      using program_limit_exceeded::program_limit_exceeded;\n   };\n\n   struct object_not_in_prerequisite_state  // 55xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct object_in_use  // 55006\n      : object_not_in_prerequisite_state\n   {\n      using object_not_in_prerequisite_state::object_not_in_prerequisite_state;\n   };\n\n   struct cant_change_runtime_param  // 55P02\n      : object_not_in_prerequisite_state\n   {\n      using object_not_in_prerequisite_state::object_not_in_prerequisite_state;\n   };\n\n   struct lock_not_available  // 55P03\n      : object_not_in_prerequisite_state\n   {\n      using object_not_in_prerequisite_state::object_not_in_prerequisite_state;\n   };\n\n   struct unsafe_new_enum_value_usage  // 55P04\n      : object_not_in_prerequisite_state\n   {\n      using object_not_in_prerequisite_state::object_not_in_prerequisite_state;\n   };\n\n   struct operator_intervention  // 57xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct query_canceled  // 57014\n      : operator_intervention\n   {\n      using operator_intervention::operator_intervention;\n   };\n\n   struct admin_shutdown  // 57P01\n      : operator_intervention\n   {\n      using operator_intervention::operator_intervention;\n   };\n\n   struct crash_shutdown  // 57P02\n      : operator_intervention\n   {\n      using operator_intervention::operator_intervention;\n   };\n\n   struct cannot_connect_now  // 57P03\n      : operator_intervention\n   {\n      using operator_intervention::operator_intervention;\n   };\n\n   struct database_dropped  // 57P04\n      : operator_intervention\n   {\n      using operator_intervention::operator_intervention;\n   };\n\n   struct system_error  // 58xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct io_error  // 58030\n      : system_error\n   {\n      using system_error::system_error;\n   };\n\n   struct undefined_file  // 58P01\n      : system_error\n   {\n      using system_error::system_error;\n   };\n\n   struct duplicate_file  // 58P02\n      : system_error\n   {\n      using system_error::system_error;\n   };\n\n   struct snapshot_too_old  // 72xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct config_file_error  // F0xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct lock_file_exists  // F0001\n      : config_file_error\n   {\n      using config_file_error::config_file_error;\n   };\n\n   struct fdw_error  // HVxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct fdw_out_of_memory  // HV001\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_dynamic_parameter_value_needed  // HV002\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_data_type  // HV004\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_column_name_not_found  // HV005\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_data_type_descriptors  // HV006\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_column_name  // HV007\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_column_number  // HV008\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_use_of_null_pointer  // HV009\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_string_format  // HV00A\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_handle  // HV00B\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_option_index  // HV00C\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_option_name  // HV00D\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_option_name_not_found  // HV00J\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_reply_handle  // HV00K\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_unable_to_create_execution  // HV00L\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_unable_to_create_reply  // HV00M\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_unable_to_establish_connection  // HV00N\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_no_schemas  // HV00P\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_schema_not_found  // HV00Q\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_table_not_found  // HV00R\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_function_sequence_error  // HV010\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_too_many_handles  // HV014\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_inconsistent_descriptor_information  // HV021\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_attribute_value  // HV024\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_string_length_or_buffer_length  // HV090\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct fdw_invalid_descriptor_field_identifier  // HV091\n      : fdw_error\n   {\n      using fdw_error::fdw_error;\n   };\n\n   struct plpgsql_error  // P0xxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct raise_exception  // P0001\n      : plpgsql_error\n   {\n      using plpgsql_error::plpgsql_error;\n   };\n\n   struct no_data_found  // P0002\n      : plpgsql_error\n   {\n      using plpgsql_error::plpgsql_error;\n   };\n\n   struct too_many_rows  // P0003\n      : plpgsql_error\n   {\n      using plpgsql_error::plpgsql_error;\n   };\n\n   struct assert_failure  // P0004\n      : plpgsql_error\n   {\n      using plpgsql_error::plpgsql_error;\n   };\n\n   struct internal_error  // XXxxx\n      : sql_error\n   {\n      using sql_error::sql_error;\n   };\n\n   struct data_corrupted  // XX001\n      : internal_error\n   {\n      using internal_error::internal_error;\n   };\n\n   struct index_corrupted  // XX002\n      : internal_error\n   {\n      using internal_error::internal_error;\n   };\n\n   namespace internal\n   {\n      [[noreturn]] void throw_sqlstate( PGresult* pgresult );\n      [[noreturn]] void throw_sqlstate( const char* error_message, const std::string_view sql_state );\n\n   }  // namespace internal\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/field.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_FIELD_HPP\n#define TAO_PQ_FIELD_HPP\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include <tao/pq/null.hpp>\n#include <tao/pq/result_traits.hpp>\n\nnamespace tao::pq\n{\n   class row;\n\n   class field\n   {\n   private:\n      friend class row;\n\n      const row* m_row = nullptr;\n      std::size_t m_column = 0;\n\n      field() = default;\n\n      field( const row& row, const std::size_t column ) noexcept\n         : m_row( &row ),\n           m_column( column )\n      {}\n\n   public:\n      [[nodiscard]] auto name() const -> std::string;\n      [[nodiscard]] auto index() const noexcept -> std::size_t;\n\n      [[nodiscard]] auto is_null() const -> bool;\n      [[nodiscard]] auto get() const -> const char*;\n\n      template< result_type T >\n         requires( result_traits_size< T > == 1 )\n      [[nodiscard]] auto as() const -> T;  // implemented in row.hpp\n\n      template< result_type T >\n         requires( result_traits_size< T > == 1 )\n      [[nodiscard]] auto optional() const\n      {\n         return as< std::optional< T > >();\n      }\n\n      [[nodiscard]] auto operator==( null_t /*unused*/ ) const\n      {\n         return is_null();\n      }\n\n      friend void swap( field& lhs, field& rhs ) noexcept\n      {\n         std::swap( lhs.m_row, rhs.m_row );\n         std::swap( lhs.m_column, rhs.m_column );\n      }\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/aggregate.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_AGGREGATE_HPP\n#define TAO_PQ_INTERNAL_AGGREGATE_HPP\n\n#include <tuple>\n#include <type_traits>\n\n#if !( ( __cpp_structured_bindings >= 202400L ) && ( __cplusplus >= 202400L ) )\n#include <cstddef>\n#include <utility>\n#endif\n\nnamespace tao::pq::internal\n{\n\n   // clang-format off\n\n#if ( __cpp_structured_bindings >= 202400L ) && ( __cplusplus >= 202400L )\n\n   template< typename T >\n      requires( std::is_aggregate_v< T > && !std::is_empty_v< T > && !std::is_union_v< T > )\n   constexpr auto tie_aggregate( const T& value ) noexcept\n   {\n      const auto& [ ...values ] = value;\n      return std::tie( values... );\n   }\n\n#else\n\n   // clang-format on\n\n   struct convert_to_any\n   {\n      template< typename T >\n      constexpr operator T() const noexcept;\n   };\n\n   template< std::size_t >\n   using indexed_convert_to_any = convert_to_any;\n\n   template< typename, typename >\n   inline constexpr bool check_aggregate_args_impl = false;\n\n   template< typename T, std::size_t... Is >\n      requires requires { T{ std::declval< indexed_convert_to_any< Is > >()... }; }\n   inline constexpr bool check_aggregate_args_impl< T, std::index_sequence< Is... > > = true;\n\n   template< typename T, std::size_t N >\n   inline constexpr bool check_aggregate_args = check_aggregate_args_impl< T, std::make_index_sequence< N > >;\n\n   template< typename T, std::size_t N = 1 >\n   inline constexpr std::size_t minimum_aggregate_args = minimum_aggregate_args< T, N + 1 >;\n\n   template< typename T, std::size_t N >\n      requires check_aggregate_args< T, N >\n   inline constexpr std::size_t minimum_aggregate_args< T, N > = N;\n\n   template< typename T, std::size_t N = minimum_aggregate_args< T > >\n   inline constexpr std::size_t count_aggregate_args = N - 1;\n\n   template< typename T, std::size_t N >\n      requires check_aggregate_args< T, N >\n   inline constexpr std::size_t count_aggregate_args< T, N > = count_aggregate_args< T, N + 1 >;\n\n#define TAO_PQ_TIE( N, ... )               \\\n   if constexpr( cnt == N ) {              \\\n      const auto& [ __VA_ARGS__ ] = value; \\\n      return std::tie( __VA_ARGS__ );      \\\n   }                                       \\\n   else                                    \\\n      static_assert( true )\n\n#define TAO_PQ_10( P ) P##0, P##1, P##2, P##3, P##4, P##5, P##6, P##7, P##8, P##9\n#define TAO_PQ_TIE10( N, ... )                               \\\n   TAO_PQ_TIE( N, __VA_ARGS__ );                             \\\n   TAO_PQ_TIE( N + 1, __VA_ARGS__, a );                      \\\n   TAO_PQ_TIE( N + 2, __VA_ARGS__, a, b );                   \\\n   TAO_PQ_TIE( N + 3, __VA_ARGS__, a, b, c );                \\\n   TAO_PQ_TIE( N + 4, __VA_ARGS__, a, b, c, d );             \\\n   TAO_PQ_TIE( N + 5, __VA_ARGS__, a, b, c, d, e );          \\\n   TAO_PQ_TIE( N + 6, __VA_ARGS__, a, b, c, d, e, f );       \\\n   TAO_PQ_TIE( N + 7, __VA_ARGS__, a, b, c, d, e, f, g );    \\\n   TAO_PQ_TIE( N + 8, __VA_ARGS__, a, b, c, d, e, f, g, h ); \\\n   TAO_PQ_TIE( N + 9, __VA_ARGS__, a, b, c, d, e, f, g, h, i );\n\n   template< typename T >\n      requires( std::is_aggregate_v< T > && !std::is_empty_v< T > && !std::is_union_v< T > )\n   constexpr auto tie_aggregate( const T& value ) noexcept\n   {\n      constexpr auto cnt = count_aggregate_args< T >;\n\n      TAO_PQ_TIE( 1, a );\n      TAO_PQ_TIE( 2, a, b );\n      TAO_PQ_TIE( 3, a, b, c );\n      TAO_PQ_TIE( 4, a, b, c, d );\n      TAO_PQ_TIE( 5, a, b, c, d, e );\n      TAO_PQ_TIE( 6, a, b, c, d, e, f );\n      TAO_PQ_TIE( 7, a, b, c, d, e, f, g );\n      TAO_PQ_TIE( 8, a, b, c, d, e, f, g, h );\n      TAO_PQ_TIE( 9, a, b, c, d, e, f, g, h, i );\n\n      TAO_PQ_TIE10( 10, TAO_PQ_10( a ) );\n      TAO_PQ_TIE10( 20, TAO_PQ_10( a ), TAO_PQ_10( b ) );\n      TAO_PQ_TIE10( 30, TAO_PQ_10( a ), TAO_PQ_10( b ), TAO_PQ_10( c ) );\n      TAO_PQ_TIE10( 40, TAO_PQ_10( a ), TAO_PQ_10( b ), TAO_PQ_10( c ), TAO_PQ_10( d ) );\n      TAO_PQ_TIE10( 50, TAO_PQ_10( a ), TAO_PQ_10( b ), TAO_PQ_10( c ), TAO_PQ_10( d ), TAO_PQ_10( e ) );\n      TAO_PQ_TIE10( 60, TAO_PQ_10( a ), TAO_PQ_10( b ), TAO_PQ_10( c ), TAO_PQ_10( d ), TAO_PQ_10( e ), TAO_PQ_10( f ) );\n      TAO_PQ_TIE10( 70, TAO_PQ_10( a ), TAO_PQ_10( b ), TAO_PQ_10( c ), TAO_PQ_10( d ), TAO_PQ_10( e ), TAO_PQ_10( f ), TAO_PQ_10( g ) );\n      TAO_PQ_TIE10( 80, TAO_PQ_10( a ), TAO_PQ_10( b ), TAO_PQ_10( c ), TAO_PQ_10( d ), TAO_PQ_10( e ), TAO_PQ_10( f ), TAO_PQ_10( g ), TAO_PQ_10( h ) );\n      TAO_PQ_TIE10( 90, TAO_PQ_10( a ), TAO_PQ_10( b ), TAO_PQ_10( c ), TAO_PQ_10( d ), TAO_PQ_10( e ), TAO_PQ_10( f ), TAO_PQ_10( g ), TAO_PQ_10( h ), TAO_PQ_10( i ) );\n   }\n\n#undef TAO_PQ_TIE\n#undef TAO_PQ_10\n#undef TAO_PQ_TIE10\n\n#endif\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/demangle.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_DEMANGLE_HPP\n#define TAO_PQ_INTERNAL_DEMANGLE_HPP\n\n#include <string>\n#include <typeinfo>\n\nnamespace tao::pq::internal\n{\n   [[nodiscard]] auto demangle( const char* const symbol ) -> std::string;\n\n   [[nodiscard]] inline auto demangle( const std::type_info& type_info )\n   {\n      return demangle( type_info.name() );\n   }\n\n   template< typename T >\n   [[nodiscard]] auto demangle()\n   {\n      return internal::demangle( typeid( T ) );\n   }\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/exclusive_scan.hpp",
    "content": "// Copyright (c) 2019-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_EXCLUSIVE_SCAN_HPP\n#define TAO_PQ_INTERNAL_EXCLUSIVE_SCAN_HPP\n\n#include <cstddef>\n#include <utility>\n\nnamespace tao::pq::internal\n{\n   template< typename S, typename = std::make_index_sequence< S::size() > >\n   struct exclusive_scan;\n\n   template< typename T, T... Ns, std::size_t... Is >\n   struct exclusive_scan< std::integer_sequence< T, Ns... >, std::index_sequence< Is... > >\n   {\n      template< std::size_t I >\n      static constexpr T partial_sum = ( T( 0 ) + ... + ( ( Is < I ) ? Ns : T( 0 ) ) );\n\n      using type = std::integer_sequence< T, partial_sum< Is >... >;\n   };\n\n   template< typename S >\n   using exclusive_scan_t = typename exclusive_scan< S >::type;\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/format_as.hpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_FORMAT_AS_HPP\n#define TAO_PQ_INTERNAL_FORMAT_AS_HPP\n\n#include <format>\n#include <ostream>\n#include <utility>\n\ntemplate< typename T >\n   requires requires { taopq_format_as( std::declval< T >() ); }\nstruct std::formatter< T > : std::formatter< decltype( taopq_format_as( std::declval< T >() ) ) >\n{\n   auto format( const T& v, auto& ctx ) const\n   {\n      return std::formatter< decltype( taopq_format_as( v ) ) >::format( taopq_format_as( v ), ctx );\n   }\n};\n\ntemplate< typename T >\n   requires requires { taopq_format_as( std::declval< T >() ); }\nauto operator<<( std::ostream& os, const T& v ) -> std::ostream&\n{\n   return os << taopq_format_as( v );\n}\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/from_chars.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_FROM_CHARS_HPP\n#define TAO_PQ_INTERNAL_FROM_CHARS_HPP\n\n#include <charconv>\n#include <format>\n#include <stdexcept>\n#include <string_view>\n#include <system_error>\n\n#include <tao/pq/internal/demangle.hpp>\n#include <tao/pq/internal/unreachable.hpp>\n\nnamespace tao::pq::internal\n{\n   template< typename T >\n   [[nodiscard]] auto from_chars( const std::string_view value ) -> T\n   {\n      T result;\n      const auto [ ptr, ec ] = std::from_chars( value.data(), value.data() + value.size(), result );\n      if( ec == std::errc() ) {\n         if( ptr == value.data() + value.size() ) {\n            return result;\n         }\n      }\n      const auto type = internal::demangle< T >();\n      if( ( ec == std::errc() ) || ( ec == std::errc::invalid_argument ) ) {\n         throw std::invalid_argument( std::format( \"tao::pq::internal::from_chars<{}>(): {}\", type, value ) );\n      }\n      if( ec == std::errc::result_out_of_range ) {\n         throw std::out_of_range( std::format( \"tao::pq::internal::from_chars<{}>(): {}\", type, value ) );\n      }\n      TAO_PQ_INTERNAL_UNREACHABLE;  // LCOV_EXCL_LINE\n   }\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/gen.hpp",
    "content": "// Copyright (c) 2019-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_GEN_HPP\n#define TAO_PQ_INTERNAL_GEN_HPP\n\n#include <cstddef>\n#include <utility>\n\n#include <tao/pq/internal/exclusive_scan.hpp>\n\nnamespace tao::pq::internal\n{\n   template< typename, typename, typename >\n   struct make;\n\n   template< std::size_t... Is,\n             std::size_t... Js,\n             std::size_t... Ns >\n   struct make< std::index_sequence< Is... >,\n                std::index_sequence< Js... >,\n                std::index_sequence< Ns... > >\n   {\n      template< std::size_t I >\n      static constexpr std::size_t outer = ( 0 + ... + ( ( Ns <= I ) ? 1 : 0 ) ) - 1;\n\n      template< std::size_t J >\n      static constexpr std::size_t select = ( 0 + ... + ( ( Js == J ) ? Ns : 0 ) );\n\n      template< std::size_t I >\n      static constexpr std::size_t inner = I - select< outer< I > >;\n\n      using outer_sequence = std::index_sequence< outer< Is >... >;\n      using inner_sequence = std::index_sequence< inner< Is >... >;\n   };\n\n   template< std::size_t... Ns >\n   using gen = make< std::make_index_sequence< ( 0 + ... + Ns ) >,\n                     std::make_index_sequence< sizeof...( Ns ) >,\n                     exclusive_scan_t< std::index_sequence< Ns... > > >;\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/parameter_traits_helper.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_PARAMETER_TRAITS_HELPER_HPP\n#define TAO_PQ_INTERNAL_PARAMETER_TRAITS_HELPER_HPP\n\n#include <cassert>\n#include <charconv>\n#include <cstddef>\n#include <string>\n#include <system_error>\n\n#include <tao/pq/oid.hpp>\n\nnamespace tao::pq::internal\n{\n   struct char_pointer_helper\n   {\n   protected:\n      const char* const m_p;\n\n   public:\n      explicit char_pointer_helper( const char* p ) noexcept\n         : m_p( p )\n      {}\n\n      static constexpr std::size_t columns = 1;\n      static constexpr bool self_contained = false;\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto type() noexcept -> oid\n      {\n         return oid::invalid;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] constexpr auto value() const noexcept -> const char*\n      {\n         return m_p;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto length() noexcept -> int\n      {\n         return 0;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto format() noexcept -> int\n      {\n         return 0;\n      }\n   };\n\n   // note the size of the buffer is hardcoded\n   struct buffer_helper\n   {\n   protected:\n      char m_buffer[ 32 ];\n\n   public:\n      static constexpr std::size_t columns = 1;\n      static constexpr bool self_contained = true;\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto type() noexcept -> oid\n      {\n         return oid::invalid;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] auto value() const noexcept -> const char*\n      {\n         return m_buffer;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto length() noexcept -> int\n      {\n         return 0;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto format() noexcept -> int\n      {\n         return 0;\n      }\n\n      template< std::size_t I >\n      void element( std::string& data ) const\n      {\n         data += m_buffer;\n      }\n\n      template< std::size_t I >\n      void copy_to( std::string& data ) const\n      {\n         data += m_buffer;\n      }\n   };\n\n   // all uses of this helper need to fit into the buffer, see above\n   struct to_chars_helper\n      : buffer_helper\n   {\n      explicit to_chars_helper( const auto v ) noexcept\n      {\n         const auto [ ptr, ec ] = std::to_chars( std::begin( m_buffer ), std::end( m_buffer ), v );\n         assert( ec == std::errc() );\n         *ptr = '\\0';\n      }\n   };\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/poll.hpp",
    "content": "// Copyright (c) 2023-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_POLL_HPP\n#define TAO_PQ_INTERNAL_POLL_HPP\n\n#include <tao/pq/poll.hpp>\n\nnamespace tao::pq::internal\n{\n   [[nodiscard]] auto poll( const int socket, const bool wait_for_write, const int timeout_ms ) -> pq::poll::status;\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/pool.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_POOL_HPP\n#define TAO_PQ_INTERNAL_POOL_HPP\n\n#include <atomic>\n#include <cassert>\n#include <cstddef>\n#include <list>\n#include <memory>\n#include <mutex>\n#include <utility>\n\nnamespace tao::pq::internal\n{\n   template< typename T >\n   class pool\n      : public std::enable_shared_from_this< pool< T > >\n   {\n   private:\n      std::list< std::shared_ptr< T > > m_items;\n      mutable std::mutex m_mutex;\n      std::atomic< std::size_t > m_attached = 0;\n\n      struct deleter final\n      {\n         std::weak_ptr< pool > m_pool;\n\n         deleter() = default;\n\n         explicit deleter( std::weak_ptr< pool >&& p ) noexcept\n            : m_pool( std::move( p ) )\n         {}\n\n         void operator()( T* item ) const noexcept\n         {\n            std::unique_ptr< T > up( item );\n            if( const auto p = m_pool.lock() ) {\n               --( p->m_attached );\n               p->push( up );\n            }\n         }\n      };\n\n   protected:\n      pool() = default;\n      virtual ~pool() = default;\n\n      // create a new T\n      [[nodiscard]] virtual auto v_create() const -> std::unique_ptr< T > = 0;\n      [[nodiscard]] virtual auto v_is_valid( T& ) const noexcept -> bool = 0;\n\n      void push( std::unique_ptr< T >& up ) noexcept\n      {\n         if( this->v_is_valid( *up ) ) {\n            std::shared_ptr< T > sp( up.release(), deleter() );\n            const std::lock_guard lock( m_mutex );\n            // potentially throws -> calls abort() due to noexcept!\n            m_items.emplace_back( std::move( sp ) );\n         }\n      }\n\n      [[nodiscard]] auto pull() noexcept\n      {\n         std::shared_ptr< T > nrv;\n         const std::lock_guard lock( m_mutex );\n         if( !m_items.empty() ) {\n            nrv = std::move( m_items.back() );\n            m_items.pop_back();\n         }\n         return nrv;\n      }\n\n   public:\n      pool( const pool& ) = delete;\n      pool( pool&& ) = delete;\n      void operator=( const pool& ) = delete;\n      void operator=( pool&& ) = delete;\n\n      static void attach( const std::shared_ptr< T >& sp, std::weak_ptr< pool >&& p ) noexcept\n      {\n         const auto d = std::get_deleter< deleter >( sp );\n         assert( d );\n         if( const auto o = d->m_pool.lock() ) {\n            --( o->m_attached );\n         }\n         d->m_pool = std::move( p );\n         if( const auto n = d->m_pool.lock() ) {\n            ++( n->m_attached );\n         }\n      }\n\n      static void detach( const std::shared_ptr< T >& sp ) noexcept\n      {\n         const auto d = std::get_deleter< deleter >( sp );\n         assert( d );\n         if( const auto o = d->m_pool.lock() ) {\n            --( o->m_attached );\n         }\n         d->m_pool.reset();\n      }\n\n      // create a new T which is put into the pool when no longer used\n      [[nodiscard]] auto create() -> std::shared_ptr< T >\n      {\n         const std::shared_ptr< T > c{ v_create().release(), pool::deleter( this->weak_from_this() ) };\n         ++m_attached;\n         return c;\n      }\n\n      // get an instance from the pool or create a new one if necessary\n      [[nodiscard]] auto get() -> std::shared_ptr< T >\n      {\n         while( const auto sp = pull() ) {\n            if( this->v_is_valid( *sp ) ) {\n               const auto d = std::get_deleter< deleter >( sp );\n               assert( d );\n               d->m_pool = this->weak_from_this();\n               ++m_attached;\n               return sp;\n            }\n         }\n         return create();\n      }\n\n      [[nodiscard]] auto empty() const noexcept -> bool\n      {\n         const std::lock_guard lock( m_mutex );\n         return m_items.empty();\n      }\n\n      [[nodiscard]] auto size() const noexcept -> std::size_t\n      {\n         const std::lock_guard lock( m_mutex );\n         return m_items.size();\n      }\n\n      [[nodiscard]] auto attached() const noexcept -> std::size_t\n      {\n         return m_attached;\n      }\n\n      void erase_invalid()\n      {\n         std::list< std::shared_ptr< T > > deferred_delete;\n         const std::lock_guard lock( m_mutex );\n         auto it = m_items.begin();\n         while( it != m_items.end() ) {\n            if( !this->v_is_valid( **it ) ) {\n               deferred_delete.splice( deferred_delete.end(), m_items, it++ );\n            }\n            else {\n               ++it;\n            }\n         }\n      }\n   };\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/resize_uninitialized.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_RESIZE_UNINITIALIZED_HPP\n#define TAO_PQ_INTERNAL_RESIZE_UNINITIALIZED_HPP\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\nnamespace tao::pq::internal\n{\n   // the below uses a hack to call private member functions of a class, described here:\n   // https://github.com/facebook/folly/blob/master/folly/memory/UninitializedMemoryHacks.h\n\n   namespace  // NOLINT(google-build-namespaces)\n   {\n      struct odr_helper;\n\n      void resize_uninitialized_proxy( std::string& v, const std::size_t n ) noexcept;\n\n#if defined( _LIBCPP_STRING )\n\n      // ...create a proxy to generate the actual implementation of the above function...\n      template< typename T, auto F >\n      struct string_proxy\n      {\n         // ...define the function declared above...\n         friend void resize_uninitialized_proxy( T& v, const std::size_t n ) noexcept\n         {\n            ( v.*F )( n );  // v.__set_size( n );\n            v[ v.size() ] = typename T::value_type( 0 );\n         }\n      };\n\n      // ...and here's the actual \"trick\": an explicit template instantiation skips the access checks,\n      // so you can reference private members and forward them to the above proxy!\n      template struct string_proxy< std::string, &std::string::__set_size >;\n\n#elif defined( _GLIBCXX_STRING ) && _GLIBCXX_USE_CXX11_ABI  // NOLINT(misc-include-cleaner)\n\n      template< typename T, auto F >\n      struct string_proxy\n      {\n         friend void resize_uninitialized_proxy( T& v, const std::size_t n ) noexcept\n         {\n            ( v.*F )( n );  // v._M_set_length( n );\n         }\n      };\n\n      template struct string_proxy< std::string, &std::string::_M_set_length >;\n\n#elif defined( _GLIBCXX_STRING )\n\n      template< typename T, auto F >\n      struct string_proxy\n      {\n         friend void resize_uninitialized_proxy( T& v, const std::size_t n ) noexcept\n         {\n            // v._M_rep()->_M_set_length_and_sharable( n );\n            ( v.*F )()->_M_set_length_and_sharable( n );\n         }\n      };\n\n      template struct string_proxy< std::string, &std::string::_M_rep >;\n\n#elif defined( _MSC_VER )\n\n      template< typename T, auto F >\n      struct string_proxy\n      {\n         friend void resize_uninitialized_proxy( T& v, const std::size_t n ) noexcept\n         {\n            ( v.*F )( n );  // v._Eos( n );\n         }\n      };\n\n      template struct string_proxy< std::string, &std::string::_Eos >;\n\n#else\n#error \"No implementation for resize_uninitialized for std::string available on this platform.\"\n#endif\n\n#if defined( _LIBCPP_VECTOR )\n\n      void resize_uninitialized_proxy( std::vector< std::byte >& v, const std::size_t n ) noexcept;\n\n      template< typename T, auto M >\n      struct vector_proxy\n      {\n         friend void resize_uninitialized_proxy( T& v, const std::size_t n ) noexcept\n         {\n            // v.__end_ = v.data() + n;\n            v.*M = v.data() + n;\n\n#ifndef _LIBCPP_HAS_NO_ASAN\n            __sanitizer_annotate_contiguous_container( v.data(),\n                                                       v.data() + v.capacity(),\n                                                       v.data() + v.size(),\n                                                       v.data() + n );\n#endif\n         }\n      };\n\n      template struct vector_proxy< std::vector< std::byte >, &std::vector< std::byte >::__end_ >;\n\n#elif defined( _GLIBCXX_VECTOR )\n\n      void resize_uninitialized_proxy( std::vector< std::byte >& v, const std::size_t n ) noexcept;\n\n      template< typename T, typename B, auto Mimpl, auto Mfinish >\n      struct vector_proxy\n      {\n         friend void resize_uninitialized_proxy( T& v, const std::size_t n ) noexcept\n         {\n            // v._M_impl._M_finish = v.data() + n;\n            reinterpret_cast< B& >( v ).*Mimpl.*Mfinish = v.data() + n;\n         }\n      };\n\n      template struct vector_proxy< std::vector< std::byte >,\n                                    std::vector< std::byte >::_Base,\n                                    &std::vector< std::byte >::_M_impl,\n                                    &decltype( std::declval< std::vector< std::byte > >()._M_impl )::_M_finish >;\n\n#elif defined( _MSC_VER )\n\n      void resize_uninitialized_proxy( std::vector< std::byte >& v, const std::size_t n ) noexcept;\n\n      template< typename T, auto Mypair, auto Myval2, auto Mylast >\n      struct vector_proxy\n      {\n         friend void resize_uninitialized_proxy( T& v, const std::size_t n ) noexcept\n         {\n            // v._Mypair._Myval2._Mylast = v.data() + n;\n            v.*Mypair.*Myval2.*Mylast = v.data() + n;\n         }\n      };\n\n      template struct vector_proxy< std::vector< std::byte >,\n                                    &std::vector< std::byte >::_Mypair,\n                                    &decltype( std::declval< std::vector< std::byte > >()._Mypair )::_Myval2,\n                                    &decltype( std::declval< std::vector< std::byte > >()._Mypair._Myval2 )::_Mylast >;\n\n#else\n\n      // generic version\n\n      struct no_init_byte\n      {\n         std::byte b;\n         no_init_byte() noexcept {}  // NOLINT(modernize-use-equals-default)\n      };\n\n      static_assert( sizeof( std::vector< std::byte > ) == sizeof( std::vector< no_init_byte > ) );\n      static_assert( alignof( std::vector< std::byte > ) == alignof( std::vector< no_init_byte > ) );\n\n      inline void resize_uninitialized_proxy( std::vector< std::byte >& v, const std::size_t n ) noexcept\n      {\n         // undefined behaviour?\n         reinterpret_cast< std::vector< no_init_byte >& >( v ).resize( n );\n      }\n\n#endif\n\n   }  // namespace\n\n   template< typename = odr_helper >\n   void resize_uninitialized( std::string& v, const std::size_t n )\n   {\n      if( n <= v.size() ) {\n         v.resize( n );\n      }\n      else {\n         if( n > v.capacity() ) {\n            v.reserve( n );\n         }\n         internal::resize_uninitialized_proxy( v, n );\n      }\n   }\n\n   template< typename = odr_helper >\n   void resize_uninitialized( std::vector< std::byte >& v, const std::size_t n )\n   {\n      if( n <= v.size() ) {\n         v.resize( n );\n      }\n      else {\n         if( n > v.capacity() ) {\n            v.reserve( n );\n         }\n         internal::resize_uninitialized_proxy( v, n );\n      }\n   }\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/strtox.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_STRTOX_HPP\n#define TAO_PQ_INTERNAL_STRTOX_HPP\n\nnamespace tao::pq::internal\n{\n   [[nodiscard]] auto strtof( const char* input ) -> float;\n   [[nodiscard]] auto strtod( const char* input ) -> double;\n   [[nodiscard]] auto strtold( const char* input ) -> long double;\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/unreachable.hpp",
    "content": "// Copyright (c) 2020-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_UNREACHABLE_HPP\n#define TAO_PQ_INTERNAL_UNREACHABLE_HPP\n\n#if defined( _MSC_VER ) && !defined( __clang__ )\n#define TAO_PQ_INTERNAL_UNREACHABLE __assume( false )\n#else\n#define TAO_PQ_INTERNAL_UNREACHABLE __builtin_unreachable()\n#endif\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/internal/zsv.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_INTERNAL_ZSV_HPP\n#define TAO_PQ_INTERNAL_ZSV_HPP\n\n#include <cstddef>\n#include <string>\n#include <string_view>\n\nnamespace tao::pq::internal\n{\n   // zero-terminated string view\n   struct zsv\n   {\n      const char* value;\n\n      zsv( std::nullptr_t ) = delete;\n\n      constexpr zsv( const char* v ) noexcept\n         : value( v )\n      {}\n\n      zsv( const std::string& v ) noexcept\n         : value( v.c_str() )\n      {}\n\n      constexpr operator const char*() const noexcept\n      {\n         return value;\n      }\n\n      constexpr operator std::string_view() const noexcept\n      {\n         return value;\n      }\n   };\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/is_aggregate.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_IS_AGGREGATE_HPP\n#define TAO_PQ_IS_AGGREGATE_HPP\n\nnamespace tao::pq\n{\n   template< typename >\n   inline constexpr bool is_aggregate = false;\n\n   template< typename T >\n   inline constexpr bool is_aggregate_result = is_aggregate< T >;\n\n   template< typename T >\n   inline constexpr bool is_aggregate_parameter = is_aggregate< T >;\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/is_array.hpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_IS_ARRAY_HPP\n#define TAO_PQ_IS_ARRAY_HPP\n\n#include <array>\n#include <cstddef>\n#include <list>\n#include <set>\n#include <span>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\nnamespace tao::pq\n{\n   namespace internal\n   {\n      template< typename >\n      inline constexpr bool is_array = false;\n\n      template< typename... Ts >\n      inline constexpr bool is_array< std::list< Ts... > > = true;\n\n      template< typename... Ts >\n      inline constexpr bool is_array< std::set< Ts... > > = true;\n\n      template< typename... Ts >\n      inline constexpr bool is_array< std::unordered_set< Ts... > > = true;\n\n      template< typename... Ts >\n      inline constexpr bool is_array< std::vector< Ts... > > = true;\n\n      template< typename... Ts >\n      inline constexpr bool is_array< std::vector< std::byte, Ts... > > = false;\n\n   }  // namespace internal\n\n   template< typename T >\n   inline constexpr bool is_array = internal::is_array< T >;\n\n   namespace internal\n   {\n      template< typename T >\n      inline constexpr bool is_array_parameter = pq::is_array< T >;\n\n      template< typename T, std::size_t N >\n      inline constexpr bool is_array_parameter< std::span< T, N > > = true;\n\n      template< std::size_t N >\n      inline constexpr bool is_array_parameter< std::span< std::byte, N > > = false;\n\n      template< std::size_t N >\n      inline constexpr bool is_array_parameter< std::span< const std::byte, N > > = false;\n\n      template< typename T, std::size_t N >\n      inline constexpr bool is_array_parameter< std::array< T, N > > = true;\n\n   }  // namespace internal\n\n   template< typename T >\n   inline constexpr bool is_array_parameter = internal::is_array_parameter< T >;\n\n   namespace internal\n   {\n      template< typename T >\n      inline constexpr bool is_array_result = pq::is_array< T >;\n\n   }  // namespace internal\n\n   template< typename T >\n   inline constexpr bool is_array_result = internal::is_array_result< T >;\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/isolation_level.hpp",
    "content": "// Copyright (c) 2020-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_ISOLATION_LEVEL_HPP\n#define TAO_PQ_ISOLATION_LEVEL_HPP\n\n#include <cstdint>\n#include <string_view>\n\n#include <tao/pq/internal/format_as.hpp>\n\nnamespace tao::pq\n{\n   enum class isolation_level : std::uint8_t\n   {\n      default_isolation_level,\n      serializable,\n      repeatable_read,\n      read_committed,\n      read_uncommitted\n   };\n\n   [[nodiscard]] constexpr auto taopq_format_as( const isolation_level il ) noexcept -> std::string_view\n   {\n      switch( il ) {\n         case isolation_level::default_isolation_level:\n            return \"default_isolation_level\";\n\n         case isolation_level::serializable:\n            return \"serializable\";\n\n         case isolation_level::repeatable_read:\n            return \"repeatable_read\";\n\n         case isolation_level::read_committed:\n            return \"read_committed\";\n\n         case isolation_level::read_uncommitted:\n            return \"read_uncommitted\";\n\n         default:\n            return \"<unknown>\";\n      }\n   }\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/large_object.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_LARGE_OBJECT_HPP\n#define TAO_PQ_LARGE_OBJECT_HPP\n\n#include <cstddef>\n#include <cstdint>\n#include <cstring>\n#include <ios>\n#include <memory>\n#include <string>\n\n#include <tao/pq/binary.hpp>\n#include <tao/pq/oid.hpp>\n\nnamespace tao::pq\n{\n   class transaction;\n\n   class large_object final\n   {\n   private:\n      std::shared_ptr< transaction > m_transaction;\n      int m_fd;\n\n   public:\n      [[nodiscard]] static auto create( const std::shared_ptr< transaction >& transaction, const oid desired_id = oid::invalid ) -> oid;\n\n      static void remove( const std::shared_ptr< transaction >& transaction, const oid id );\n\n      [[nodiscard]] static auto import_file( const std::shared_ptr< transaction >& transaction, const char* filename, const oid desired_id = oid::invalid ) -> oid;\n      static void export_file( const std::shared_ptr< transaction >& transaction, const oid id, const char* filename );\n\n      large_object( const std::shared_ptr< transaction >& transaction, const oid id, const std::ios_base::openmode m );\n\n      large_object( const large_object& ) = delete;\n      large_object( large_object&& other ) noexcept;\n\n      ~large_object();\n\n      void operator=( const large_object& ) = delete;\n      auto operator=( large_object&& rhs ) -> large_object&;  // NOLINT(performance-noexcept-move-constructor)\n\n      void close();\n\n      [[nodiscard]] auto read( char* data, const std::size_t size ) -> std::size_t;\n      void write( const char* data, const std::size_t size );\n\n      [[nodiscard]] auto read( std::byte* data, const std::size_t size ) -> std::size_t\n      {\n         return read( reinterpret_cast< char* >( data ), size );\n      }\n\n      void write( const std::byte* data, const std::size_t size )\n      {\n         write( reinterpret_cast< const char* >( data ), size );\n      }\n\n      template< typename T = binary >\n      [[nodiscard]] auto read( const std::size_t size ) -> T = delete;\n\n      void write( const char* data )\n      {\n         write( data, std::strlen( data ) );\n      }\n\n      template< typename... Ts >\n      void write( Ts&&... ts )\n      {\n         const auto bv = pq::to_binary_view( std::forward< Ts >( ts )... );\n         write( bv.data(), bv.size() );\n      }\n\n      void resize( const std::int64_t size );\n\n      auto seek( const std::int64_t offset, const std::ios_base::seekdir whence ) -> std::int64_t;\n      [[nodiscard]] auto tell() const -> std::int64_t;\n   };\n\n   template<>\n   [[nodiscard]] auto large_object::read< std::string >( const std::size_t size ) -> std::string;\n\n   template<>\n   [[nodiscard]] auto large_object::read< binary >( const std::size_t size ) -> binary;\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/log.hpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_LOG_HPP\n#define TAO_PQ_LOG_HPP\n\n#include <chrono>\n#include <functional>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/poll.hpp>\n\nnamespace tao::pq\n{\n   class connection;\n   class transaction;\n\n   struct log\n   {\n      struct connection_pool_t\n      {\n         // TODO...\n\n      } connection_pool;\n\n      struct connection_t\n      {\n         using send_query_t = std::function< void( connection&, const char* statement, int n_params, const Oid types[], const char* const values[], const int lengths[], const int formats[] ) >;\n         using send_query_result_t = std::function< void( connection&, int result ) >;\n\n         using send_query_prepared_t = std::function< void( connection&, const char* statement, int n_params, const char* const values[], const int lengths[], const int formats[] ) >;\n         using send_query_prepared_result_t = std::function< void( connection&, int result ) >;\n\n         using wait_t = std::function< void( connection&, bool wait_for_write, std::chrono::steady_clock::time_point end ) >;\n\n         using poll_t = std::function< void( connection&, int socket, bool wait_for_write, int timeout_ms ) >;\n         using poll_result_t = std::function< void( connection&, int socket, poll::status status ) >;\n\n         using is_busy_result_t = std::function< void( const connection&, int result ) >;  // noexcept\n\n         using consume_input_t = std::function< void( connection& ) >;\n         using consume_input_result_t = std::function< void( connection&, int result ) >;\n\n         using flush_t = std::function< void( connection& ) >;\n         using flush_result_t = std::function< void( connection&, int result ) >;\n\n         using get_result_t = std::function< void( connection&, std::chrono::steady_clock::time_point end ) >;\n         using get_result_result_t = std::function< void( connection&, PGresult* ) >;\n\n         using enter_pipeline_mode_result_t = std::function< void( connection&, int result ) >;\n\n         using exit_pipeline_mode_t = std::function< void( connection& ) >;\n         using exit_pipeline_mode_result_t = std::function< void( connection&, int result ) >;\n\n         using pipeline_sync_t = std::function< void( connection& ) >;\n         using pipeline_sync_result_t = std::function< void( connection&, int result ) >;\n\n         struct : send_query_t\n         {\n            send_query_result_t result;\n            using send_query_t::operator=;\n         } send_query;\n\n         struct : send_query_prepared_t\n         {\n            send_query_prepared_result_t result;\n            using send_query_prepared_t::operator=;\n         } send_query_prepared;\n\n         wait_t wait;\n\n         struct : poll_t\n         {\n            poll_result_t result;\n            using poll_t::operator=;\n         } poll;\n\n         struct\n         {\n            is_busy_result_t result;\n         } is_busy;\n\n         struct : consume_input_t\n         {\n            consume_input_result_t result;\n            using consume_input_t::operator=;\n         } consume_input;\n\n         struct : flush_t\n         {\n            flush_result_t result;\n            using flush_t::operator=;\n         } flush;\n\n         struct : get_result_t\n         {\n            get_result_result_t result;\n            using get_result_t::operator=;\n         } get_result;\n\n         struct\n         {\n            enter_pipeline_mode_result_t result;\n         } enter_pipeline_mode;\n\n         struct : exit_pipeline_mode_t\n         {\n            exit_pipeline_mode_result_t result;\n            using exit_pipeline_mode_t::operator=;\n         } exit_pipeline_mode;\n\n         struct : pipeline_sync_t\n         {\n            pipeline_sync_result_t result;\n            using pipeline_sync_t::operator=;\n         } pipeline_sync;\n\n      } connection;\n\n      struct transaction_t\n      {\n         // check std::current_exception() for more information\n         using destructor_rollback_failed_t = std::function< void( transaction& ) >;  // noexcept\n\n         destructor_rollback_failed_t destructor_rollback_failed;\n\n      } transaction;\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/notification.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_NOTIFICATION_HPP\n#define TAO_PQ_NOTIFICATION_HPP\n\n#include <cassert>\n#include <memory>\n\n#include <libpq-fe.h>\n\nnamespace tao::pq\n{\n   class connection;\n\n   class notification final\n   {\n   private:\n      friend connection;\n\n      const std::unique_ptr< PGnotify, decltype( &PQfreemem ) > m_pgnotify;\n\n      explicit notification( PGnotify* notify ) noexcept\n         : m_pgnotify( notify, &PQfreemem )\n      {\n         assert( notify );\n      }\n\n   public:\n      [[nodiscard]] auto channel() const noexcept -> const char*\n      {\n         return m_pgnotify->relname;\n      }\n\n      [[nodiscard]] auto payload() const noexcept -> const char*\n      {\n         return m_pgnotify->extra;\n      }\n\n      [[nodiscard]] auto underlying_raw_ptr() noexcept -> PGnotify*\n      {\n         return m_pgnotify.get();\n      }\n\n      [[nodiscard]] auto underlying_raw_ptr() const noexcept -> const PGnotify*\n      {\n         return m_pgnotify.get();\n      }\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/null.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_NULL_HPP\n#define TAO_PQ_NULL_HPP\n\nnamespace tao::pq\n{\n   struct null_t final\n   {\n      explicit constexpr null_t( int /*unused*/ ) {}\n   };\n\n   inline constexpr null_t null{ 0 };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/oid.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_OID_HPP\n#define TAO_PQ_OID_HPP\n\n#include <libpq-fe.h>\n\nnamespace tao::pq\n{\n   static_assert( InvalidOid == 0 );\n\n   enum class oid : Oid  // NOLINT(performance-enum-size)\n   {\n      invalid = 0,\n      bytea = 17,\n      text = 25\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/parameter.hpp",
    "content": "// Copyright (c) 2023-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_PARAMETER_HPP\n#define TAO_PQ_PARAMETER_HPP\n\n#include <cstddef>\n#include <memory>\n#include <stdexcept>\n#include <type_traits>\n#include <utility>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/parameter_traits.hpp>\n\nnamespace tao::pq\n{\n   class transaction;\n\n   template< std::size_t Max = 16 >\n   class parameter;\n\n   namespace internal\n   {\n      template< typename >\n      inline constexpr bool is_parameter = false;\n\n      template< std::size_t Max >\n      inline constexpr bool is_parameter< parameter< Max > > = true;\n\n      template< typename... As >\n      inline constexpr std::size_t parameter_size = ( parameter_size< std::decay_t< As > > + ... + 0 );\n\n      template< typename A >\n      inline constexpr std::size_t parameter_size< A > = parameter_traits< A >::columns;\n\n      template< std::size_t Max >\n      inline constexpr std::size_t parameter_size< parameter< Max > > = Max;\n\n   }  // namespace internal\n\n   template< typename T >\n   concept parameter_type_dynamic = internal::is_parameter< std::decay_t< T > >;\n\n   template< typename T >\n   concept parameter_type = parameter_type_direct< T > || parameter_type_dynamic< T >;\n\n   template< std::size_t Max >\n   class parameter\n   {\n   private:\n      struct vbase\n      {\n         vbase() noexcept = default;\n         virtual ~vbase() = default;\n\n         vbase( const vbase& ) = delete;\n         vbase( vbase&& ) = delete;\n\n         void operator=( const vbase& ) = delete;\n         void operator=( vbase&& ) = delete;\n      };\n\n      template< typename T >\n      struct binder : vbase\n      {\n         const parameter_traits< T > m_traits;\n\n         explicit binder( const T& t ) noexcept( noexcept( parameter_traits< T >( t ) ) )\n            : m_traits( t )\n         {}\n      };\n\n      template< typename T >\n      struct holder : vbase\n      {\n         const T m_value;\n         const parameter_traits< T > m_traits;\n\n         explicit holder( T&& t ) noexcept( noexcept( parameter_traits< T >( t ) ) && std::is_nothrow_move_constructible_v< T > )\n            : m_value( std::move( t ) ),\n              m_traits( m_value )\n         {}\n      };\n\n      std::size_t m_pos = 0;\n      vbase* m_params[ Max ];\n\n      int m_size = 0;\n      Oid m_types[ Max ];\n      const char* m_values[ Max ];\n      int m_lengths[ Max ];\n      int m_formats[ Max ];\n\n      template< std::size_t >\n      friend class parameter;\n\n      friend class transaction_base;\n\n      template< std::size_t... Is >\n      void fill( const auto& t, std::index_sequence< Is... > /*unused*/ )\n      {\n         ( ( m_types[ m_size + Is ] = static_cast< Oid >( t.template type< Is >() ) ), ... );\n         ( ( m_values[ m_size + Is ] = t.template value< Is >() ), ... );\n         ( ( m_lengths[ m_size + Is ] = t.template length< Is >() ), ... );\n         ( ( m_formats[ m_size + Is ] = t.template format< Is >() ), ... );\n      }\n\n      template< parameter_type_direct A >\n      void bind_impl( A&& a )\n      {\n         using D = std::decay_t< A&& >;\n\n         constexpr auto columns = parameter_traits< D >::columns;\n         if( ( static_cast< std::size_t >( m_size ) + columns ) > Max ) {\n            throw std::length_error( \"too many parameters!\" );\n         }\n\n         constexpr auto hold = std::is_rvalue_reference_v< A&& > && !parameter_traits< D >::self_contained;\n         using container_t = std::conditional_t< hold, holder< D >, binder< D > >;\n\n         auto bptr = std::make_unique< container_t >( std::forward< A >( a ) );\n         parameter::fill( bptr->m_traits, std::make_index_sequence< columns >() );\n\n         m_params[ m_pos++ ] = bptr.release();\n         m_size += columns;\n      }\n\n      template< std::size_t N >\n      void bind_impl( const parameter< N >& p )\n      {\n         const std::size_t columns = p.m_size;\n         if( m_size + columns > Max ) {\n            throw std::length_error( \"too many parameters!\" );\n         }\n\n         for( std::size_t n = 0; n < columns; ++n ) {\n            m_types[ m_size + n ] = p.m_types[ n ];\n            m_values[ m_size + n ] = p.m_values[ n ];\n            m_lengths[ m_size + n ] = p.m_lengths[ n ];\n            m_formats[ m_size + n ] = p.m_formats[ n ];\n         }\n\n         m_size += static_cast< int >( columns );\n      }\n\n      template< std::size_t N >\n      void bind_impl( parameter< N >& p )\n      {\n         bind_impl( const_cast< const parameter< N >& >( p ) );\n      }\n\n      template< std::size_t N >\n      void bind_impl( parameter< N >&& p ) = delete;  // NOLINT(modernize-use-equals-delete)\n\n   public:\n      template< parameter_type... As >\n      explicit parameter( As&&... as ) noexcept( noexcept( std::declval< parameter >().bind( std::forward< As >( as )... ) ) )\n      {\n         parameter::bind( std::forward< As >( as )... );\n      }\n\n      ~parameter()\n      {\n#if defined( __GNUC__ ) && !defined( __clang__ )\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wmaybe-uninitialized\"\n#endif\n         for( std::size_t i = 0; i != m_pos; ++i ) {\n            delete m_params[ i ];\n         }\n#if defined( __GNUC__ ) && !defined( __clang__ )\n#pragma GCC diagnostic pop\n#endif\n      }\n\n      explicit parameter( const parameter& p )\n      {\n         parameter::bind( p );\n      }\n\n      explicit parameter( parameter&& p ) = delete;\n\n      void operator=( const parameter& ) = delete;\n      void operator=( parameter&& ) = delete;\n\n      template< parameter_type... As >\n      void bind( As&&... as ) noexcept( sizeof...( As ) == 0 )\n      {\n         ( parameter::bind_impl( std::forward< As >( as ) ), ... );\n      }\n\n      template< parameter_type... As >\n      void reset( As&&... as ) noexcept( noexcept( std::declval< parameter >().bind( std::forward< As >( as )... ) ) )\n      {\n         for( std::size_t i = 0; i != m_pos; ++i ) {\n            delete m_params[ i ];\n         }\n         m_pos = 0;\n         m_size = 0;\n         parameter::bind( std::forward< As >( as )... );\n      }\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/parameter_traits.hpp",
    "content": "// Copyright (c) 2020-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_PARAMETER_TRAITS_HPP\n#define TAO_PQ_PARAMETER_TRAITS_HPP\n\n#include <cassert>\n#include <cmath>\n#include <cstddef>\n#include <cstdio>\n#include <cstring>\n#include <span>\n#include <string>\n#include <string_view>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include <tao/pq/binary.hpp>\n#include <tao/pq/bind.hpp>\n#include <tao/pq/internal/parameter_traits_helper.hpp>\n#include <tao/pq/internal/resize_uninitialized.hpp>\n#include <tao/pq/null.hpp>\n#include <tao/pq/oid.hpp>\n\nnamespace tao::pq\n{\n   template< typename >\n   struct parameter_traits;\n\n   namespace internal\n   {\n      // helper for arrays\n      void array_append( std::string& buffer, std::string_view data );\n\n      // helper for table_writer\n      void table_writer_append( std::string& buffer, std::string_view data );\n\n      template< std::size_t N >\n      void snprintf( char ( &buffer )[ N ], const char* format, const auto v ) noexcept\n      {\n         static_assert( N >= 32 );\n         if( std::isfinite( v ) ) {\n            [[maybe_unused]] const auto result = std::snprintf( buffer, N, format, v );\n            assert( result > 0 );\n            assert( static_cast< std::size_t >( result ) < N );\n         }\n         else if( std::isnan( v ) ) {\n#if defined( _MSC_VER )\n            [[maybe_unused]] const auto result = strncpy_s( buffer, \"NAN\", N );\n            assert( result == 0 );\n#else\n            std::strcpy( buffer, \"NAN\" );  // NOLINT(clang-analyzer-security.insecureAPI.strcpy)\n#endif\n         }\n         else {\n#if defined( _MSC_VER )\n            [[maybe_unused]] const auto result = strncpy_s( buffer, ( v < 0 ) ? \"-INF\" : \"INF\", N );\n            assert( result == 0 );\n#else\n            std::strcpy( buffer, ( v < 0 ) ? \"-INF\" : \"INF\" );  // NOLINT(clang-analyzer-security.insecureAPI.strcpy)\n#endif\n         }\n      }\n\n      template< typename T >\n      concept parameter_type_has_element = requires( const parameter_traits< std::decay_t< T > >& t, std::string& s ) {\n         { t.template element< 0 >( s ) } -> std::same_as< void >;\n      };\n\n   }  // namespace internal\n\n   template< typename T >\n   concept parameter_type_direct = requires( const parameter_traits< std::decay_t< T > >& t, std::string& s ) {\n      { parameter_traits< std::decay_t< T > >::columns } -> std::same_as< const std::size_t& >;\n      { parameter_traits< std::decay_t< T > >::self_contained } -> std::same_as< const bool& >;\n      { t.template type< 0 >() } -> std::same_as< oid >;\n      { t.template value< 0 >() } -> std::same_as< const char* >;\n      { t.template length< 0 >() } -> std::same_as< int >;\n      { t.template format< 0 >() } -> std::same_as< int >;\n      { t.template copy_to< 0 >( s ) } -> std::same_as< void >;\n   } && ( ( parameter_traits< std::decay_t< T > >::columns >= 2 ) || internal::parameter_type_has_element< T > );\n\n   template<>\n   struct parameter_traits< null_t >\n   {\n      explicit parameter_traits( null_t /*unused*/ ) noexcept\n      {}\n\n      static constexpr std::size_t columns = 1;\n      static constexpr bool self_contained = true;\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto type() noexcept -> oid\n      {\n         return oid::invalid;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto value() noexcept -> const char*\n      {\n         return nullptr;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto length() noexcept -> int\n      {\n         return 0;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto format() noexcept -> int\n      {\n         return 0;\n      }\n\n      template< std::size_t I >\n      static void element( std::string& data )\n      {\n         data += \"NULL\";\n      }\n\n      template< std::size_t I >\n      static void copy_to( std::string& data )\n      {\n         data += \"\\\\N\";\n      }\n   };\n\n   template<>\n   struct parameter_traits< bool >\n      : internal::char_pointer_helper\n   {\n      static constexpr bool self_contained = true;\n\n      explicit parameter_traits( const bool v ) noexcept\n         : internal::char_pointer_helper( v ? \"TRUE\" : \"FALSE\" )\n      {}\n\n      template< std::size_t I >\n      void element( std::string& data ) const\n      {\n         data += m_p;\n      }\n\n      template< std::size_t I >\n      void copy_to( std::string& data ) const\n      {\n         data += m_p;\n      }\n   };\n\n   template<>\n   struct parameter_traits< char >\n   {\n      const char m_value[ 2 ];  // NOLINT(modernize-use-default-member-init)\n\n      explicit parameter_traits( const char v ) noexcept\n         : m_value{ v, '\\0' }\n      {}\n\n      static constexpr std::size_t columns = 1;\n      static constexpr bool self_contained = true;\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto type() noexcept -> oid\n      {\n         return oid::invalid;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] auto value() const noexcept -> const char*\n      {\n         return m_value;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto length() noexcept -> int\n      {\n         return 0;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto format() noexcept -> int\n      {\n         return 0;\n      }\n\n      template< std::size_t I >\n      void element( std::string& data ) const\n      {\n         internal::array_append( data, std::string_view( m_value, 1 ) );\n      }\n\n      template< std::size_t I >\n      void copy_to( std::string& data ) const\n      {\n         internal::table_writer_append( data, std::string_view( m_value, 1 ) );\n      }\n   };\n\n   template<>\n   struct parameter_traits< signed char >\n      : internal::to_chars_helper\n   {\n      using internal::to_chars_helper::to_chars_helper;\n   };\n\n   template<>\n   struct parameter_traits< unsigned char >\n      : internal::to_chars_helper\n   {\n      using internal::to_chars_helper::to_chars_helper;\n   };\n\n   template<>\n   struct parameter_traits< short >\n      : internal::to_chars_helper\n   {\n      using internal::to_chars_helper::to_chars_helper;\n   };\n\n   template<>\n   struct parameter_traits< unsigned short >\n      : internal::to_chars_helper\n   {\n      using internal::to_chars_helper::to_chars_helper;\n   };\n\n   template<>\n   struct parameter_traits< int >\n      : internal::to_chars_helper\n   {\n      using internal::to_chars_helper::to_chars_helper;\n   };\n\n   template<>\n   struct parameter_traits< unsigned int >\n      : internal::to_chars_helper\n   {\n      using internal::to_chars_helper::to_chars_helper;\n   };\n\n   template<>\n   struct parameter_traits< long >\n      : internal::to_chars_helper\n   {\n      using internal::to_chars_helper::to_chars_helper;\n   };\n\n   template<>\n   struct parameter_traits< unsigned long >\n      : internal::to_chars_helper\n   {\n      using internal::to_chars_helper::to_chars_helper;\n   };\n\n   template<>\n   struct parameter_traits< long long >\n      : internal::to_chars_helper\n   {\n      using internal::to_chars_helper::to_chars_helper;\n   };\n\n   template<>\n   struct parameter_traits< unsigned long long >\n      : internal::to_chars_helper\n   {\n      using internal::to_chars_helper::to_chars_helper;\n   };\n\n   template<>\n   struct parameter_traits< float >\n      : internal::buffer_helper\n   {\n      explicit parameter_traits( const float v ) noexcept\n      {\n         internal::snprintf( m_buffer, \"%.9g\", v );\n      }\n   };\n\n   template<>\n   struct parameter_traits< double >\n      : internal::buffer_helper\n   {\n      explicit parameter_traits( const double v ) noexcept\n      {\n         internal::snprintf( m_buffer, \"%.17g\", v );\n      }\n   };\n\n   template<>\n   struct parameter_traits< long double >\n      : internal::buffer_helper\n   {\n      explicit parameter_traits( const long double v ) noexcept\n      {\n         internal::snprintf( m_buffer, \"%.21Lg\", v );\n      }\n   };\n\n   template<>\n   struct parameter_traits< const char* >\n      : internal::char_pointer_helper\n   {\n      using internal::char_pointer_helper::char_pointer_helper;\n\n      template< std::size_t I >\n      void element( std::string& data ) const\n      {\n         internal::array_append( data, m_p );\n      }\n\n      template< std::size_t I >\n      void copy_to( std::string& data ) const\n      {\n         internal::table_writer_append( data, m_p );\n      }\n   };\n\n   // for string_views (which are not zero-terminated) we can use binary format and,\n   // surprisingly, it does not seem to cause any issues\n\n   template<>\n   struct parameter_traits< std::string_view >\n   {\n   private:\n      const std::string_view m_v;\n\n   public:\n      explicit parameter_traits( const std::string_view v ) noexcept\n         : m_v( v )\n      {}\n\n      static constexpr std::size_t columns = 1;\n      static constexpr bool self_contained = false;\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto type() noexcept -> oid\n      {\n         return oid::text;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] auto value() const noexcept -> const char*\n      {\n         return m_v.data();\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] auto length() const noexcept -> int\n      {\n         return static_cast< int >( m_v.size() );\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto format() noexcept -> int\n      {\n         return 1;\n      }\n\n      template< std::size_t I >\n      void element( std::string& data ) const\n      {\n         internal::array_append( data, m_v );\n      }\n\n      template< std::size_t I >\n      void copy_to( std::string& data ) const\n      {\n         internal::table_writer_append( data, m_v );\n      }\n   };\n\n   template<>\n   struct parameter_traits< std::string >\n      : parameter_traits< std::string_view >\n   {\n      using parameter_traits< std::string_view >::parameter_traits;\n   };\n\n   template< std::size_t Extent >\n   struct parameter_traits< std::span< const std::byte, Extent > >\n   {\n   private:\n      const std::span< const std::byte, Extent > m_v;\n\n   public:\n      explicit parameter_traits( const std::span< const std::byte, Extent > v ) noexcept\n         : m_v( v )\n      {}\n\n      static constexpr std::size_t columns = 1;\n      static constexpr bool self_contained = false;\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto type() noexcept -> oid\n      {\n         return oid::bytea;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] constexpr auto value() const noexcept -> const char*\n      {\n         return reinterpret_cast< const char* >( m_v.data() );\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] constexpr auto length() const noexcept -> int\n      {\n         return static_cast< int >( m_v.size() );\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto format() noexcept -> int\n      {\n         return 1;\n      }\n\n      template< std::size_t I >\n      void element( std::string& data ) const\n      {\n         // generate bytea hex format\n         constexpr char hex[] = \"0123456789abcdef\";\n         auto pos = data.size();\n         internal::resize_uninitialized( data, pos + 3 + ( 2 * m_v.size() ) );\n         data[ pos++ ] = '\\\\';\n         data[ pos++ ] = '\\\\';\n         data[ pos++ ] = 'x';\n         for( auto c : m_v ) {\n            data[ pos++ ] = hex[ static_cast< unsigned char >( c ) >> 4 ];\n            data[ pos++ ] = hex[ static_cast< unsigned char >( c ) & 15 ];\n         }\n      }\n\n      template< std::size_t I >\n      void copy_to( std::string& data ) const\n      {\n         element< I >( data );\n      }\n   };\n\n   template< std::size_t Extent >\n   struct parameter_traits< std::span< std::byte, Extent > >\n      : parameter_traits< std::span< const std::byte, Extent > >\n   {\n      using parameter_traits< std::span< const std::byte, Extent > >::parameter_traits;\n   };\n\n   template< typename Allocator >\n   struct parameter_traits< std::vector< std::byte, Allocator > >\n      : parameter_traits< binary_view >\n   {\n      using parameter_traits< binary_view >::parameter_traits;\n   };\n\n   // default free function to detect member function to_taopq()\n   [[nodiscard]] auto to_taopq( const auto& t ) noexcept( noexcept( t.to_taopq() ) )\n      requires requires { t.to_taopq(); }\n   {\n      return t.to_taopq();\n   }\n\n   // default free function to detect bind<T>::to_taopq()\n   template< typename T >\n   [[nodiscard]] auto to_taopq( const T& t ) noexcept( noexcept( bind< T >::to_taopq( t ) ) )\n      requires requires { bind< T >::to_taopq( t ); }\n   {\n      return bind< T >::to_taopq( t );\n   }\n\n   // note: calls to to_taopq are unqualified to enable ADL\n\n   namespace internal\n   {\n      template< typename T >\n      struct parameter_holder\n      {\n         using result_t = decltype( to_taopq( std::declval< const T& >() ) );\n         const result_t result;\n\n         explicit parameter_holder( const T& t ) noexcept( noexcept( result_t( to_taopq( t ) ) ) )\n            : result( to_taopq( t ) )\n         {}\n      };\n\n   }  // namespace internal\n\n   template< typename T >\n      requires requires( const T& t ) { to_taopq( t ); }\n   struct parameter_traits< T >\n      : private internal::parameter_holder< T >,\n        public parameter_traits< typename internal::parameter_holder< T >::result_t >\n   {\n      static constexpr bool self_contained = true;\n\n      using typename internal::parameter_holder< T >::result_t;\n\n      explicit parameter_traits( const T& t ) noexcept( noexcept( internal::parameter_holder< T >( t ),\n                                                                  parameter_traits< result_t >( std::declval< result_t >() ) ) )\n         : internal::parameter_holder< T >( t ),\n           parameter_traits< result_t >( this->result )\n      {}\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/parameter_traits_aggregate.hpp",
    "content": "// Copyright (c) 2020-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_PARAMETER_TRAITS_AGGREGATE_HPP\n#define TAO_PQ_PARAMETER_TRAITS_AGGREGATE_HPP\n\n#include <tao/pq/internal/aggregate.hpp>\n#include <tao/pq/is_aggregate.hpp>\n#include <tao/pq/parameter_traits.hpp>\n\nnamespace tao::pq\n{\n   namespace internal\n   {\n      template< typename T >\n      struct parameter_tie_aggregate\n      {\n         using result_t = decltype( internal::tie_aggregate( std::declval< const T& >() ) );\n         const result_t result;\n\n         explicit parameter_tie_aggregate( const T& t ) noexcept\n            : result( internal::tie_aggregate( t ) )\n         {}\n      };\n\n   }  // namespace internal\n\n   template< typename T >\n      requires is_aggregate_parameter< T >\n   struct parameter_traits< T >\n      : private internal::parameter_tie_aggregate< T >,\n        public parameter_traits< typename internal::parameter_tie_aggregate< T >::result_t >\n   {\n      using typename internal::parameter_tie_aggregate< T >::result_t;\n\n      explicit parameter_traits( const T& t ) noexcept( noexcept( internal::parameter_tie_aggregate< T >( t ),\n                                                                  parameter_traits< result_t >( std::declval< result_t >() ) ) )\n         : internal::parameter_tie_aggregate< T >( t ),\n           parameter_traits< result_t >( this->result )\n      {}\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/parameter_traits_array.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_PARAMETER_TRAITS_ARRAY_HPP\n#define TAO_PQ_PARAMETER_TRAITS_ARRAY_HPP\n\n#include <cstddef>\n#include <string>\n\n#include <tao/pq/is_array.hpp>\n#include <tao/pq/oid.hpp>\n#include <tao/pq/parameter_traits.hpp>\n\nnamespace tao::pq\n{\n   namespace internal\n   {\n      template< typename T >\n      concept array_parameter_type = pq::is_array_parameter< T > && ( pq::is_array_parameter< typename T::value_type > || ( parameter_traits< typename T::value_type >::columns == 1 ) );\n\n      template< typename T >\n      void to_array( std::string& data, const T& v )\n      {\n         parameter_traits< T >( v ).template element< 0 >( data );\n      }\n\n      template< typename T >\n         requires pq::is_array_parameter< T >\n      void to_array( std::string& data, const T& v )\n      {\n         data += '{';\n         if( v.empty() ) {\n            data += '}';\n         }\n         else {\n            for( const auto& e : v ) {\n               internal::to_array( data, e );\n               data += ',';\n            }\n            *data.rbegin() = '}';\n         }\n      }\n\n   }  // namespace internal\n\n   template< internal::array_parameter_type T >\n   struct parameter_traits< T >\n   {\n   private:\n      std::string m_data;\n\n   public:\n      explicit parameter_traits( const T& v )\n      {\n         internal::to_array( m_data, v );\n      }\n\n      static constexpr std::size_t columns = 1;\n      static constexpr bool self_contained = true;\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto type() noexcept -> oid\n      {\n         return oid::invalid;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] auto value() const noexcept -> const char*\n      {\n         return m_data.c_str();\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto length() noexcept -> int\n      {\n         return 0;\n      }\n\n      template< std::size_t I >\n      [[nodiscard]] static constexpr auto format() noexcept -> int\n      {\n         return 0;\n      }\n\n      template< std::size_t I >\n      void element( std::string& data ) const\n      {\n         internal::array_append( data, m_data );\n      }\n\n      template< std::size_t I >\n      void copy_to( std::string& data ) const\n      {\n         internal::table_writer_append( data, m_data );\n      }\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/parameter_traits_optional.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_PARAMETER_TRAITS_OPTIONAL_HPP\n#define TAO_PQ_PARAMETER_TRAITS_OPTIONAL_HPP\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include <tao/pq/oid.hpp>\n#include <tao/pq/parameter_traits.hpp>\n\ntemplate< typename T >\nstruct tao::pq::parameter_traits< std::optional< T > >\n{\nprivate:\n   using U = parameter_traits< std::decay_t< T > >;\n   std::optional< U > m_forwarder;\n\npublic:\n   explicit parameter_traits( const std::optional< T >& v ) noexcept( noexcept( m_forwarder.emplace( *v ) ) )\n   {\n      if( v ) {\n         m_forwarder.emplace( *v );\n      }\n   }\n\n   explicit parameter_traits( std::optional< T >&& v ) noexcept( noexcept( m_forwarder.emplace( std::move( *v ) ) ) )\n   {\n      if( v ) {\n         m_forwarder.emplace( std::move( *v ) );\n      }\n   }\n\n   static constexpr std::size_t columns = U::columns;\n   static constexpr bool self_contained = U::self_contained;\n\n   template< std::size_t I >\n   [[nodiscard]] static constexpr auto type() noexcept -> oid\n   {\n      return U::template type< I >();\n   }\n\n   template< std::size_t I >\n   [[nodiscard]] constexpr auto value() const noexcept( noexcept( m_forwarder ? m_forwarder->template value< I >() : nullptr ) ) -> const char*\n   {\n      return m_forwarder ? m_forwarder->template value< I >() : nullptr;\n   }\n\n   template< std::size_t I >\n   [[nodiscard]] constexpr auto length() const noexcept( noexcept( m_forwarder ? m_forwarder->template length< I >() : 0 ) ) -> int\n   {\n      return m_forwarder ? m_forwarder->template length< I >() : 0;\n   }\n\n   template< std::size_t I >\n   [[nodiscard]] static constexpr auto format() noexcept -> int\n   {\n      return U::template format< I >();\n   }\n\n   template< std::size_t I >\n   void element( std::string& data ) const\n   {\n      if( m_forwarder ) {\n         m_forwarder->template element< I >( data );\n      }\n      else {\n         data += \"NULL\";\n      }\n   }\n\n   template< std::size_t I >\n   void copy_to( std::string& data ) const\n   {\n      if( m_forwarder ) {\n         m_forwarder->template copy_to< I >( data );\n      }\n      else {\n         data += \"\\\\N\";\n      }\n   }\n};\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/parameter_traits_pair.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_PARAMETER_TRAITS_PAIR_HPP\n#define TAO_PQ_PARAMETER_TRAITS_PAIR_HPP\n\n#include <cstddef>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include <tao/pq/internal/gen.hpp>\n#include <tao/pq/oid.hpp>\n#include <tao/pq/parameter_traits.hpp>\n\ntemplate< typename T, typename U >\nstruct tao::pq::parameter_traits< std::pair< T, U > >\n{\nprivate:\n   using first_t = parameter_traits< std::decay_t< T > >;\n   using second_t = parameter_traits< std::decay_t< U > >;\n\n   using pair_t = std::pair< first_t, second_t >;\n   pair_t m_pair;\n\n   using gen = internal::gen< first_t::columns, second_t::columns >;\n\npublic:\n   explicit parameter_traits( const std::pair< T, U >& pair ) noexcept( noexcept( pair_t( pair ) ) )\n      : m_pair( pair )\n   {}\n\n   explicit parameter_traits( std::pair< T, U >&& pair ) noexcept( noexcept( pair_t( std::move( pair ) ) ) )\n      : m_pair( std::move( pair ) )\n   {}\n\n   static constexpr std::size_t columns = first_t::columns + second_t::columns;\n   static constexpr bool self_contained = first_t::self_contained && second_t::self_contained;\n\n   template< std::size_t I >\n   [[nodiscard]] constexpr auto type() const noexcept( noexcept( std::get< gen::template outer< I > >( m_pair ).template type< gen::template inner< I > >() ) ) -> oid\n   {\n      return std::get< gen::template outer< I > >( m_pair ).template type< gen::template inner< I > >();\n   }\n\n   template< std::size_t I >\n   [[nodiscard]] constexpr auto value() const noexcept( noexcept( std::get< gen::template outer< I > >( m_pair ).template value< gen::template inner< I > >() ) ) -> const char*\n   {\n      return std::get< gen::template outer< I > >( m_pair ).template value< gen::template inner< I > >();\n   }\n\n   template< std::size_t I >\n   [[nodiscard]] constexpr auto length() const noexcept( noexcept( std::get< gen::template outer< I > >( m_pair ).template length< gen::template inner< I > >() ) ) -> int\n   {\n      return std::get< gen::template outer< I > >( m_pair ).template length< gen::template inner< I > >();\n   }\n\n   template< std::size_t I >\n   [[nodiscard]] constexpr auto format() const noexcept( noexcept( std::get< gen::template outer< I > >( m_pair ).template format< gen::template inner< I > >() ) ) -> int\n   {\n      return std::get< gen::template outer< I > >( m_pair ).template format< gen::template inner< I > >();\n   }\n\n   template< std::size_t I >\n   void copy_to( std::string& data ) const\n   {\n      std::get< gen::template outer< I > >( m_pair ).template copy_to< gen::template inner< I > >( data );\n   }\n};\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/parameter_traits_tuple.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_PARAMETER_TRAITS_TUPLE_HPP\n#define TAO_PQ_PARAMETER_TRAITS_TUPLE_HPP\n\n#include <cstddef>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include <tao/pq/internal/gen.hpp>\n#include <tao/pq/oid.hpp>\n#include <tao/pq/parameter_traits.hpp>\n\ntemplate< typename... Ts >\n   requires( sizeof...( Ts ) != 0 )\nstruct tao::pq::parameter_traits< std::tuple< Ts... > >\n{\nprivate:\n   using tuple_t = std::tuple< parameter_traits< std::decay_t< Ts > >... >;\n   tuple_t m_tuple;\n\n   using gen = internal::gen< parameter_traits< std::decay_t< Ts > >::columns... >;\n\npublic:\n   explicit parameter_traits( const std::tuple< Ts... >& tuple ) noexcept( noexcept( tuple_t( tuple ) ) )\n      : m_tuple( tuple )\n   {}\n\n   explicit parameter_traits( std::tuple< Ts... >&& tuple ) noexcept( noexcept( tuple_t( std::move( tuple ) ) ) )\n      : m_tuple( std::move( tuple ) )\n   {}\n\n   static constexpr std::size_t columns = ( 0 + ... + parameter_traits< std::decay_t< Ts > >::columns );\n   static constexpr bool self_contained = ( parameter_traits< std::decay_t< Ts > >::self_contained && ... );\n\n   template< std::size_t I >\n   [[nodiscard]] constexpr auto type() const noexcept( noexcept( std::get< gen::template outer< I > >( m_tuple ).template type< gen::template inner< I > >() ) ) -> oid\n   {\n      return std::get< gen::template outer< I > >( m_tuple ).template type< gen::template inner< I > >();\n   }\n\n   template< std::size_t I >\n   [[nodiscard]] constexpr auto value() const noexcept( noexcept( std::get< gen::template outer< I > >( m_tuple ).template value< gen::template inner< I > >() ) ) -> const char*\n   {\n      return std::get< gen::template outer< I > >( m_tuple ).template value< gen::template inner< I > >();\n   }\n\n   template< std::size_t I >\n   [[nodiscard]] constexpr auto length() const noexcept( noexcept( std::get< gen::template outer< I > >( m_tuple ).template length< gen::template inner< I > >() ) ) -> int\n   {\n      return std::get< gen::template outer< I > >( m_tuple ).template length< gen::template inner< I > >();\n   }\n\n   template< std::size_t I >\n   [[nodiscard]] constexpr auto format() const noexcept( noexcept( std::get< gen::template outer< I > >( m_tuple ).template format< gen::template inner< I > >() ) ) -> int\n   {\n      return std::get< gen::template outer< I > >( m_tuple ).template format< gen::template inner< I > >();\n   }\n\n   template< std::size_t I >\n      requires( sizeof...( Ts ) == 1 )\n   void element( std::string& data ) const\n   {\n      std::get< gen::template outer< I > >( m_tuple ).template element< gen::template inner< I > >( data );\n   }\n\n   template< std::size_t I >\n   void copy_to( std::string& data ) const\n   {\n      std::get< gen::template outer< I > >( m_tuple ).template copy_to< gen::template inner< I > >( data );\n   }\n};\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/pipeline.hpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_PIPELINE_HPP\n#define TAO_PQ_PIPELINE_HPP\n\n#include <chrono>\n#include <memory>\n\n#include <tao/pq/transaction_base.hpp>\n\nnamespace tao::pq\n{\n   class connection;\n   class transaction;\n\n   class pipeline\n      : public transaction_base\n   {\n   private:\n      std::shared_ptr< transaction_base > m_previous;\n\n      friend class transaction;\n\n      // pass-key idiom\n      class private_key final\n      {\n         private_key() = default;\n         friend class transaction;\n      };\n\n   public:\n      pipeline( const private_key /*unused*/, const std::shared_ptr< pq::connection >& connection );\n\n      ~pipeline() override\n      {\n         try {\n            finish();\n         }\n         // LCOV_EXCL_START\n         catch( ... ) {  // NOLINT(bugprone-empty-catch)\n            // TODO: How to handle this case properly?\n         }\n         // LCOV_EXCL_STOP\n      }\n\n      pipeline( const pipeline& ) = delete;\n      pipeline( pipeline&& ) = delete;\n      void operator=( const pipeline& ) = delete;\n      void operator=( pipeline&& ) = delete;\n\n      void sync();\n      void consume_sync( const std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now() );\n\n      void finish();\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/pipeline_status.hpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_PIPELINE_STATUS_HPP\n#define TAO_PQ_PIPELINE_STATUS_HPP\n\n#include <cstdint>\n#include <string_view>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/internal/format_as.hpp>\n\nnamespace tao::pq\n{\n   enum class pipeline_status : std::uint8_t\n   {\n      on = PQ_PIPELINE_ON,\n      off = PQ_PIPELINE_OFF,\n      aborted = PQ_PIPELINE_ABORTED\n   };\n\n   [[nodiscard]] constexpr auto taopq_format_as( const pipeline_status ps ) noexcept -> std::string_view\n   {\n      switch( ps ) {\n         case pipeline_status::on:\n            return \"on\";\n\n         case pipeline_status::off:\n            return \"off\";\n\n         case pipeline_status::aborted:\n            return \"aborted\";\n\n         default:\n            return \"<unknown>\";\n      }\n   }\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/poll.hpp",
    "content": "// Copyright (c) 2023-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_POLL_HPP\n#define TAO_PQ_POLL_HPP\n\n#include <cstdint>\n#include <string_view>\n\n#include <tao/pq/internal/format_as.hpp>\n\nnamespace tao::pq::poll\n{\n   enum class status : std::uint8_t\n   {\n      timeout,\n      readable,\n      writable,\n      again\n   };\n\n   [[nodiscard]] constexpr auto taopq_format_as( const status st ) noexcept -> std::string_view\n   {\n      switch( st ) {\n         case status::timeout:\n            return \"timeout\";\n\n         case status::readable:\n            return \"readable\";\n\n         case status::writable:\n            return \"writable\";\n\n         case status::again:\n            return \"again\";\n\n         default:\n            return \"<unknown>\";\n      }\n   }\n\n   using callback = status( const int socket, const bool wait_for_write, const int timeout_ms );\n\n}  // namespace tao::pq::poll\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/result.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_RESULT_HPP\n#define TAO_PQ_RESULT_HPP\n\n#include <cassert>\n#include <cstddef>\n#include <cstdint>\n#include <format>\n#include <iterator>\n#include <list>\n#include <map>\n#include <memory>\n#include <optional>\n#include <set>\n#include <stdexcept>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/internal/zsv.hpp>\n#include <tao/pq/result_status.hpp>\n#include <tao/pq/row.hpp>\n\nnamespace tao::pq\n{\n   class connection;\n   class table_reader;\n   class table_writer;\n   class transaction_base;\n\n   class result final\n   {\n   private:\n      friend class connection;\n      friend class table_reader;\n      friend class table_writer;\n      friend class transaction_base;\n\n      const std::shared_ptr< PGresult > m_pgresult;\n      const std::size_t m_columns;\n      const std::size_t m_rows;\n\n      void check_row( const std::size_t row ) const;\n\n      explicit result( PGresult* pgresult );\n\n   public:\n      [[nodiscard]] auto status() const noexcept -> result_status;\n      [[nodiscard]] auto has_rows_affected() const noexcept -> bool;\n      [[nodiscard]] auto rows_affected() const -> std::size_t;\n\n      [[nodiscard]] auto columns() const noexcept -> std::size_t\n      {\n         return m_columns;\n      }\n\n      [[nodiscard]] auto name( const std::size_t column ) const -> std::string;\n      [[nodiscard]] auto index( const internal::zsv in_name ) const -> std::size_t;\n\n      [[nodiscard]] auto size() const noexcept -> std::size_t\n      {\n         assert( m_columns != 0 );\n         return m_rows;\n      }\n\n      [[nodiscard]] auto empty() const noexcept -> bool\n      {\n         return size() == 0;\n      }\n\n   private:\n      class const_iterator\n         : private row\n      {\n      private:\n         friend class result;\n\n         explicit const_iterator( const row& r ) noexcept\n            : row( r )\n         {}\n\n      public:\n         using difference_type = std::int32_t;\n         using value_type = const row;\n         using pointer = const row*;\n         using reference = const row&;\n         using iterator_category = std::random_access_iterator_tag;\n\n         const_iterator() = default;\n\n         auto operator++() noexcept -> const_iterator&\n         {\n            ++m_row;\n            return *this;\n         }\n\n         auto operator++( int ) noexcept -> const_iterator\n         {\n            const_iterator nrv( *this );\n            ++*this;\n            return nrv;\n         }\n\n         auto operator+=( const difference_type n ) noexcept -> const_iterator&\n         {\n            m_row += n;\n            return *this;\n         }\n\n         auto operator--() noexcept -> const_iterator&\n         {\n            --m_row;\n            return *this;\n         }\n\n         auto operator--( int ) noexcept -> const_iterator\n         {\n            const_iterator nrv( *this );\n            --*this;\n            return nrv;\n         }\n\n         auto operator-=( const difference_type n ) noexcept -> const_iterator&\n         {\n            m_row -= n;\n            return *this;\n         }\n\n         [[nodiscard]] auto operator*() const noexcept -> const row&\n         {\n            return *this;\n         }\n\n         [[nodiscard]] auto operator->() const noexcept -> const row*\n         {\n            return this;\n         }\n\n         [[nodiscard]] auto operator[]( const difference_type n ) const noexcept -> row\n         {\n            return *( *this + n );\n         }\n\n         friend void swap( const_iterator& lhs, const_iterator& rhs ) noexcept\n         {\n            swap( static_cast< row& >( lhs ), static_cast< row& >( rhs ) );\n         }\n\n         [[nodiscard]] friend auto operator+( const const_iterator& lhs, const difference_type rhs ) noexcept -> const_iterator\n         {\n            const_iterator nrv( lhs );\n            nrv += rhs;\n            return nrv;\n         }\n\n         [[nodiscard]] friend auto operator+( const difference_type lhs, const const_iterator& rhs ) noexcept -> const_iterator\n         {\n            const_iterator nrv( rhs );\n            nrv += lhs;\n            return nrv;\n         }\n\n         [[nodiscard]] friend auto operator-( const const_iterator& lhs, const difference_type rhs ) noexcept -> const_iterator\n         {\n            const_iterator nrv( lhs );\n            nrv -= rhs;\n            return nrv;\n         }\n\n         [[nodiscard]] friend auto operator-( const const_iterator& lhs, const const_iterator& rhs ) noexcept -> difference_type\n         {\n            return static_cast< difference_type >( lhs.m_row ) - static_cast< difference_type >( rhs.m_row );\n         }\n\n         [[nodiscard]] friend auto operator==( const const_iterator& lhs, const const_iterator& rhs ) noexcept\n         {\n            return lhs.m_row == rhs.m_row;\n         }\n\n         [[nodiscard]] friend auto operator<=>( const const_iterator& lhs, const const_iterator& rhs ) noexcept\n         {\n            return lhs.m_row <=> rhs.m_row;\n         }\n      };\n\n   public:\n      [[nodiscard]] auto begin() const noexcept -> const_iterator;\n      [[nodiscard]] auto end() const noexcept -> const_iterator;\n\n      [[nodiscard]] auto cbegin() const noexcept\n      {\n         return begin();\n      }\n\n      [[nodiscard]] auto cend() const noexcept\n      {\n         return end();\n      }\n\n      [[nodiscard]] auto is_null( const std::size_t row, const std::size_t column ) const -> bool;\n      [[nodiscard]] auto get( const std::size_t row, const std::size_t column ) const -> const char*;\n\n      [[nodiscard]] auto operator[]( const std::size_t row ) const noexcept\n      {\n         return pq::row( *this, row, 0, m_columns );\n      }\n\n      [[nodiscard]] auto at( const std::size_t row ) const -> pq::row;\n\n      template< result_type T >\n      [[nodiscard]] auto as() const -> T\n      {\n         switch( size() ) {\n            case 0:\n               throw std::runtime_error( \"invalid empty result, expected 1 row\" );\n\n            case 1:\n               return ( *this )[ 0 ].as< T >();\n\n            default:\n               throw std::runtime_error( std::format( \"invalid result size: {} rows, expected 1 row\", m_rows ) );\n         }\n      }\n\n      template< result_type T >\n      [[nodiscard]] auto optional() const -> std::optional< T >\n      {\n         switch( size() ) {\n            case 0:\n               return std::nullopt;\n\n            case 1:\n               return ( *this )[ 0 ].as< T >();\n\n            default:\n               throw std::runtime_error( std::format( \"invalid result size: {} rows, expected 0 or 1 rows\", m_rows ) );\n         }\n      }\n\n      template< result_type T, result_type U >\n      [[nodiscard]] auto pair() const\n      {\n         return as< std::pair< T, U > >();\n      }\n\n      template< result_type... Ts >\n      [[nodiscard]] auto tuple() const\n      {\n         return as< std::tuple< Ts... > >();\n      }\n\n      template< typename T >\n         requires result_type< typename T::value_type >\n      [[nodiscard]] auto as_container() const -> T\n      {\n         assert( m_columns != 0 );\n         T nrv;\n         if constexpr( requires { nrv.reserve( size() ); } ) {\n            nrv.reserve( size() );\n         }\n         for( const auto& row : *this ) {\n            nrv.insert( nrv.end(), row.as< typename T::value_type >() );\n         }\n         return nrv;\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::vector< Ts... >::value_type >\n      [[nodiscard]] auto vector() const\n      {\n         return as_container< std::vector< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::list< Ts... >::value_type >\n      [[nodiscard]] auto list() const\n      {\n         return as_container< std::list< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::set< Ts... >::value_type >\n      [[nodiscard]] auto set() const\n      {\n         return as_container< std::set< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::multiset< Ts... >::value_type >\n      [[nodiscard]] auto multiset() const\n      {\n         return as_container< std::multiset< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::unordered_set< Ts... >::value_type >\n      [[nodiscard]] auto unordered_set() const\n      {\n         return as_container< std::unordered_set< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::unordered_multiset< Ts... >::value_type >\n      [[nodiscard]] auto unordered_multiset() const\n      {\n         return as_container< std::unordered_multiset< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::map< Ts... >::value_type >\n      [[nodiscard]] auto map() const\n      {\n         return as_container< std::map< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::multimap< Ts... >::value_type >\n      [[nodiscard]] auto multimap() const\n      {\n         return as_container< std::multimap< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::unordered_map< Ts... >::value_type >\n      [[nodiscard]] auto unordered_map() const\n      {\n         return as_container< std::unordered_map< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::unordered_multimap< Ts... >::value_type >\n      [[nodiscard]] auto unordered_multimap() const\n      {\n         return as_container< std::unordered_multimap< Ts... > >();\n      }\n\n      [[nodiscard]] auto underlying_raw_ptr() noexcept -> PGresult*\n      {\n         return m_pgresult.get();\n      }\n\n      [[nodiscard]] auto underlying_raw_ptr() const noexcept -> const PGresult*\n      {\n         return m_pgresult.get();\n      }\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/result_status.hpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_RESULT_STATUS_HPP\n#define TAO_PQ_RESULT_STATUS_HPP\n\n#include <cstdint>\n#include <string_view>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/internal/format_as.hpp>\n\nnamespace tao::pq\n{\n   enum class result_status : std::uint8_t\n   {\n      empty_query = PGRES_EMPTY_QUERY,\n      command_ok = PGRES_COMMAND_OK,\n      tuples_ok = PGRES_TUPLES_OK,\n      copy_out = PGRES_COPY_OUT,\n      copy_in = PGRES_COPY_IN,\n      bad_response = PGRES_BAD_RESPONSE,\n      nonfatal_error = PGRES_NONFATAL_ERROR,\n      fatal_error = PGRES_FATAL_ERROR,\n      single_tuple = PGRES_SINGLE_TUPLE,\n#if defined( LIBPQ_HAS_CHUNK_MODE )\n      tuples_chunk = PGRES_TUPLES_CHUNK,\n#endif\n      pipeline_sync = PGRES_PIPELINE_SYNC,\n      pipeline_aborted = PGRES_PIPELINE_ABORTED\n   };\n\n   [[nodiscard]] constexpr auto taopq_format_as( const result_status rs ) noexcept -> std::string_view\n   {\n      switch( rs ) {\n         case result_status::empty_query:\n            return \"empty_query\";\n\n         case result_status::command_ok:\n            return \"command_ok\";\n\n         case result_status::tuples_ok:\n            return \"tuples_ok\";\n\n         case result_status::copy_out:\n            return \"copy_out\";\n\n         case result_status::copy_in:\n            return \"copy_in\";\n\n         case result_status::bad_response:\n            return \"bad_response\";\n\n         case result_status::nonfatal_error:\n            return \"nonfatal_error\";\n\n         case result_status::fatal_error:\n            return \"fatal_error\";\n\n         case result_status::single_tuple:\n            return \"single_tuple\";\n\n#if defined( LIBPQ_HAS_CHUNK_MODE )\n         case result_status::tuples_chunk:\n            return \"tuples_chunk\";\n#endif\n\n         case result_status::pipeline_sync:\n            return \"pipeline_sync\";\n\n         case result_status::pipeline_aborted:\n            return \"pipeline_aborted\";\n\n         default:\n            return \"<unknown>\";\n      }\n   }\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/result_traits.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_RESULT_TRAITS_HPP\n#define TAO_PQ_RESULT_TRAITS_HPP\n\n#include <cstddef>\n#include <string>\n#include <string_view>\n#include <type_traits>\n#include <utility>\n\n#include <tao/pq/binary.hpp>\n#include <tao/pq/bind.hpp>\n#include <tao/pq/internal/exclusive_scan.hpp>\n#include <tao/pq/is_aggregate.hpp>\n\nnamespace tao::pq\n{\n   template< typename >\n   struct result_traits;\n\n   template< typename >\n   inline constexpr std::size_t result_traits_size = 1;\n\n   template< typename T >\n      requires requires { result_traits< T >::size; }\n   inline constexpr std::size_t result_traits_size< T > = result_traits< T >::size;\n\n   template< typename T >\n   concept result_type_direct = ( ( result_traits_size< T > == 1 ) && !is_aggregate_result< T > ) && requires( const char* s ) {\n      { result_traits< T >::from( s ) } -> std::same_as< T >;\n   };\n\n   class row;\n\n   template< typename T >\n   concept result_type_composite = ( ( result_traits_size< T > != 1 ) || is_aggregate_result< T > ) && requires( const row& r ) {\n      { result_traits< T >::from( r ) } -> std::same_as< T >;\n   };\n\n   template< typename T >\n   concept result_type = result_type_direct< T > || result_type_composite< T >;\n\n   template<>\n   struct result_traits< const char* >\n   {\n      [[nodiscard]] static auto from( const char* value )\n      {\n         return value;\n      }\n   };\n\n   template<>\n   struct result_traits< std::string_view >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> std::string_view\n      {\n         return value;\n      }\n   };\n\n   template<>\n   struct result_traits< bool >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> bool;\n   };\n\n   template<>\n   struct result_traits< char >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> char;\n   };\n\n   template<>\n   struct result_traits< signed char >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> signed char;\n   };\n\n   template<>\n   struct result_traits< unsigned char >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> unsigned char;\n   };\n\n   template<>\n   struct result_traits< short >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> short;\n   };\n\n   template<>\n   struct result_traits< unsigned short >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> unsigned short;\n   };\n\n   template<>\n   struct result_traits< int >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> int;\n   };\n\n   template<>\n   struct result_traits< unsigned >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> unsigned;\n   };\n\n   template<>\n   struct result_traits< long >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> long;\n   };\n\n   template<>\n   struct result_traits< unsigned long >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> unsigned long;\n   };\n\n   template<>\n   struct result_traits< long long >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> long long;\n   };\n\n   template<>\n   struct result_traits< unsigned long long >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> unsigned long long;\n   };\n\n   template<>\n   struct result_traits< float >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> float;\n   };\n\n   template<>\n   struct result_traits< double >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> double;\n   };\n\n   template<>\n   struct result_traits< long double >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> long double;\n   };\n\n   template<>\n   struct result_traits< std::string >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> std::string\n      {\n         return value;\n      }\n   };\n\n   template<>\n   struct result_traits< binary >\n   {\n      [[nodiscard]] static auto from( const char* value ) -> binary;\n   };\n\n   namespace internal\n   {\n      template< typename, typename, typename >\n      struct from_taopq;\n\n      template< typename T, typename R, typename... As >\n      struct from_taopq< T, R, R( As... ) >\n      {\n         static constexpr std::size_t size = ( 0 + ... + result_traits_size< std::decay_t< As > > );\n\n         template< typename Row, std::size_t... Ns >\n         [[nodiscard]] static auto from( const Row& row, std::index_sequence< Ns... > /*unused*/ ) -> R\n         {\n            return T::from_taopq( row.template get< std::decay_t< As > >( Ns )... );\n         }\n\n         template< typename Row >\n         [[nodiscard]] static auto from( const Row& row ) -> R\n         {\n            return from_taopq::from( row, exclusive_scan_t< std::index_sequence< result_traits_size< std::decay_t< As > >... > >() );\n         }\n      };\n\n      template< typename T, typename R, typename... As >\n      struct from_taopq< T, R, R( As... ) noexcept >\n         : from_taopq< T, R, R( As... ) >\n      {};\n\n   }  // namespace internal\n\n   template< typename T >\n      requires requires { T::from_taopq; }\n   struct result_traits< T >\n      : internal::from_taopq< T, T, decltype( T::from_taopq ) >\n   {};\n\n   template< typename T >\n      requires requires { bind< T >::from_taopq; }\n   struct result_traits< T >\n      : internal::from_taopq< bind< T >, T, decltype( bind< T >::from_taopq ) >\n   {};\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/result_traits_aggregate.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_RESULT_TRAITS_AGGREGATE_HPP\n#define TAO_PQ_RESULT_TRAITS_AGGREGATE_HPP\n\n#include <cstddef>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include <tao/pq/internal/aggregate.hpp>\n#include <tao/pq/internal/exclusive_scan.hpp>\n#include <tao/pq/is_aggregate.hpp>\n#include <tao/pq/result_traits.hpp>\n\nnamespace tao::pq\n{\n   namespace internal\n   {\n      template< typename... Ts >\n      struct decay_tuple;\n\n      template< typename... Ts >\n      struct decay_tuple< std::tuple< Ts... > >\n      {\n         using type = std::tuple< std::decay_t< Ts >... >;\n      };\n\n      template< typename T, typename = typename decay_tuple< decltype( internal::tie_aggregate( std::declval< T >() ) ) >::type >\n      struct aggregate_result;\n\n      template< typename T, typename... Ts >\n      struct aggregate_result< T, std::tuple< Ts... > >\n      {\n         static constexpr std::size_t size = ( 0 + ... + result_traits_size< Ts > );\n\n         template< typename Row, std::size_t... Ns >\n         [[nodiscard]] static auto from( const Row& row, std::index_sequence< Ns... > /*unused*/ )\n         {\n            return T{ row.template get< Ts >( Ns )... };\n         }\n\n         template< typename Row >\n         [[nodiscard]] static auto from( const Row& row )\n         {\n            return aggregate_result::from( row, exclusive_scan_t< std::index_sequence< result_traits_size< Ts >... > >() );\n         }\n      };\n\n   }  // namespace internal\n\n   template< typename T >\n      requires is_aggregate_result< T >\n   struct result_traits< T >\n      : internal::aggregate_result< T >\n   {};\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/result_traits_array.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_RESULT_TRAITS_ARRAY_HPP\n#define TAO_PQ_RESULT_TRAITS_ARRAY_HPP\n\n#include <stdexcept>\n#include <string>\n\n#include <tao/pq/is_array.hpp>\n#include <tao/pq/result_traits.hpp>\n\nnamespace tao::pq\n{\n   namespace internal\n   {\n      template< typename T >\n      concept array_result_type = pq::is_array_result< T > && ( pq::is_array_result< typename T::value_type > || ( result_traits_size< typename T::value_type > == 1 ) );\n\n      [[nodiscard]] auto parse_quoted( const char*& value ) -> std::string;\n      [[nodiscard]] auto parse_unquoted( const char*& value ) -> std::string;\n\n      template< typename T >\n      [[nodiscard]] auto parse( const char*& value ) -> T\n      {\n         if( *value == '\"' ) {\n            const std::string input = parse_quoted( ++value );\n            return result_traits< T >::from( input.c_str() );\n         }\n\n         const std::string input = parse_unquoted( value );\n         if( input == \"NULL\" ) {\n            if constexpr( requires { result_traits< T >::null(); } ) {\n               return result_traits< T >::null();\n            }\n            else {\n               throw std::invalid_argument( \"unexpected NULL value\" );\n            }\n         }\n         return result_traits< T >::from( input.c_str() );\n      }\n\n      template< typename T >\n         requires pq::is_array_result< T >\n      [[nodiscard]] auto parse( const char*& value ) -> T\n      {\n         if( *value++ != '{' ) {\n            throw std::invalid_argument( \"expected '{'\" );\n         }\n\n         T container;\n         if( *value == '}' ) {\n            ++value;\n            return container;\n         }\n\n         while( true ) {\n            using value_type = typename T::value_type;\n            if constexpr( requires { container.push_back( parse< value_type >( value ) ); } ) {\n               container.push_back( parse< value_type >( value ) );\n            }\n            else {\n               container.insert( parse< value_type >( value ) );\n            }\n            switch( *value++ ) {\n               case ',':\n               case ';':\n                  break;\n\n               case '}':\n                  return container;\n\n               default:\n                  throw std::invalid_argument( \"expected ',', ';', or '}'\" );\n            }\n         }\n      }\n\n   }  // namespace internal\n\n   template< internal::array_result_type T >\n   struct result_traits< T >\n   {\n      static auto from( const char* value ) -> T\n      {\n         const auto container = internal::parse< T >( value );\n         if( *value != '\\0' ) {\n            throw std::invalid_argument( \"unexpected additional data\" );\n         }\n         return container;\n      }\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/result_traits_optional.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_RESULT_TRAITS_OPTIONAL_HPP\n#define TAO_PQ_RESULT_TRAITS_OPTIONAL_HPP\n\n#include <cstddef>\n#include <optional>\n\n#include <tao/pq/result_traits.hpp>\n\ntemplate< typename T >\nstruct tao::pq::result_traits< std::optional< T > >\n{\n   static constexpr std::size_t size = result_traits_size< T >;\n\n   [[nodiscard]] static auto null() noexcept -> std::optional< T >\n   {\n      return std::nullopt;\n   }\n\n   [[nodiscard]] static auto from( const char* value ) -> std::optional< T >\n   {\n      return result_traits< T >::from( value );\n   }\n\n   template< typename Row >\n   [[nodiscard]] static auto from( const Row& row ) -> std::optional< T >\n   {\n      for( std::size_t column = 0; column < row.columns(); ++column ) {\n         if( !row.is_null( column ) ) {\n            return result_traits< T >::from( row );\n         }\n      }\n      return null();\n   }\n};\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/result_traits_pair.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_RESULT_TRAITS_PAIR_HPP\n#define TAO_PQ_RESULT_TRAITS_PAIR_HPP\n\n#include <cstddef>\n#include <type_traits>\n#include <utility>\n\n#include <tao/pq/result_traits.hpp>\n\ntemplate< typename T, typename U >\nstruct tao::pq::result_traits< std::pair< T, U > >\n{\n   using DT = std::decay_t< T >;\n   using DU = std::decay_t< U >;\n\n   static constexpr std::size_t size = result_traits_size< DT > + result_traits_size< DU >;\n\n   template< typename Row >\n   [[nodiscard]] static auto from( const Row& row )\n   {\n      return std::pair< T, U >( row.template get< DT >( 0 ), row.template get< DU >( result_traits_size< DT > ) );\n   }\n};\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/result_traits_tuple.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_RESULT_TRAITS_TUPLE_HPP\n#define TAO_PQ_RESULT_TRAITS_TUPLE_HPP\n\n#include <cstddef>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include <tao/pq/internal/exclusive_scan.hpp>\n#include <tao/pq/result_traits.hpp>\n\ntemplate<>\nstruct tao::pq::result_traits< std::tuple<> >\n{\n   static constexpr std::size_t size = 0;\n};\n\ntemplate< typename T >\nstruct tao::pq::result_traits< std::tuple< T > >\n{\n   static constexpr std::size_t size = result_traits_size< T >;\n\n   template< typename U = T >\n      requires std::is_same_v< T, U > && requires { result_traits< T >::null(); }\n   [[nodiscard]] static auto null()\n      -> std::tuple< T >\n   {\n      return std::tuple< T >( result_traits< T >::null() );\n   }\n\n   [[nodiscard]] static auto from( const char* value )\n   {\n      return std::tuple< T >( result_traits< T >::from( value ) );\n   }\n};\n\ntemplate< typename... Ts >\n   requires( sizeof...( Ts ) >= 2 )\nstruct tao::pq::result_traits< std::tuple< Ts... > >\n{\n   static constexpr std::size_t size = ( 0 + ... + result_traits_size< Ts > );\n\n   template< typename Row, std::size_t... Ns >\n   [[nodiscard]] static auto from( const Row& row, std::index_sequence< Ns... > /*unused*/ )\n   {\n      return std::tuple< Ts... >( row.template get< Ts >( Ns )... );\n   }\n\n   template< typename Row >\n   [[nodiscard]] static auto from( const Row& row )\n   {\n      return result_traits::from( row, internal::exclusive_scan_t< std::index_sequence< result_traits_size< Ts >... > >() );\n   }\n};\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/row.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_ROW_HPP\n#define TAO_PQ_ROW_HPP\n\n#include <cstddef>\n#include <cstdint>\n#include <format>\n#include <iterator>\n#include <optional>\n#include <stdexcept>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include <tao/pq/field.hpp>\n#include <tao/pq/internal/demangle.hpp>\n#include <tao/pq/internal/zsv.hpp>\n#include <tao/pq/is_aggregate.hpp>\n#include <tao/pq/result_traits.hpp>\n\nnamespace tao::pq\n{\n   class result;\n\n   class row\n   {\n   protected:\n      friend class field;\n      friend class result;\n\n      const result* m_result = nullptr;\n      std::size_t m_row = 0;\n      std::size_t m_offset = 0;\n      std::size_t m_columns = 0;\n\n      row() = default;\n\n      row( const result& in_result, const std::size_t in_row, const std::size_t in_offset, const std::size_t in_columns ) noexcept\n         : m_result( &in_result ),\n           m_row( in_row ),\n           m_offset( in_offset ),\n           m_columns( in_columns )\n      {}\n\n      void ensure_column( const std::size_t column ) const;\n\n   public:\n      [[nodiscard]] auto slice( const std::size_t offset, const std::size_t in_columns ) const -> row;\n\n      [[nodiscard]] auto columns() const noexcept -> std::size_t\n      {\n         return m_columns;\n      }\n\n      [[nodiscard]] auto name( const std::size_t column ) const -> std::string;\n      [[nodiscard]] auto index( const internal::zsv in_name ) const -> std::size_t;\n\n   private:\n      class const_iterator\n         : private field\n      {\n      private:\n         friend class row;\n\n         explicit const_iterator( const field& f ) noexcept\n            : field( f )\n         {}\n\n      public:\n         using difference_type = std::int32_t;\n         using value_type = const field;\n         using pointer = const field*;\n         using reference = const field&;\n         using iterator_category = std::random_access_iterator_tag;\n\n         const_iterator() = default;\n\n         auto operator++() noexcept -> const_iterator&\n         {\n            ++m_column;\n            return *this;\n         }\n\n         auto operator++( int ) noexcept -> const_iterator\n         {\n            const_iterator nrv( *this );\n            ++*this;\n            return nrv;\n         }\n\n         auto operator+=( const difference_type n ) noexcept -> const_iterator&\n         {\n            m_column += n;\n            return *this;\n         }\n\n         auto operator--() noexcept -> const_iterator&\n         {\n            --m_column;\n            return *this;\n         }\n\n         auto operator--( int ) noexcept -> const_iterator\n         {\n            const_iterator nrv( *this );\n            --*this;\n            return nrv;\n         }\n\n         auto operator-=( const difference_type n ) noexcept -> const_iterator&\n         {\n            m_column -= n;\n            return *this;\n         }\n\n         [[nodiscard]] auto operator*() const noexcept -> const field&\n         {\n            return *this;\n         }\n\n         [[nodiscard]] auto operator->() const noexcept -> const field*\n         {\n            return this;\n         }\n\n         [[nodiscard]] auto operator[]( const difference_type n ) const noexcept -> field\n         {\n            return *( *this + n );\n         }\n\n         friend void swap( const_iterator& lhs, const_iterator& rhs ) noexcept\n         {\n            swap( static_cast< field& >( lhs ), static_cast< field& >( rhs ) );\n         }\n\n         [[nodiscard]] friend auto operator+( const const_iterator& lhs, const difference_type rhs ) noexcept -> const_iterator\n         {\n            const_iterator nrv( lhs );\n            nrv += rhs;\n            return nrv;\n         }\n\n         [[nodiscard]] friend auto operator+( const difference_type lhs, const const_iterator& rhs ) noexcept -> const_iterator\n         {\n            const_iterator nrv( rhs );\n            nrv += lhs;\n            return nrv;\n         }\n\n         [[nodiscard]] friend auto operator-( const const_iterator& lhs, const difference_type rhs ) noexcept -> const_iterator\n         {\n            const_iterator nrv( lhs );\n            nrv -= rhs;\n            return nrv;\n         }\n\n         [[nodiscard]] friend auto operator-( const const_iterator& lhs, const const_iterator& rhs ) noexcept -> difference_type\n         {\n            return static_cast< difference_type >( lhs.index() ) - static_cast< difference_type >( rhs.index() );\n         }\n\n         [[nodiscard]] friend auto operator==( const const_iterator& lhs, const const_iterator& rhs ) noexcept\n         {\n            return lhs.index() == rhs.index();\n         }\n\n         [[nodiscard]] friend auto operator<=>( const const_iterator& lhs, const const_iterator& rhs ) noexcept\n         {\n            return lhs.index() <=> rhs.index();\n         }\n      };\n\n   public:\n      [[nodiscard]] auto begin() const noexcept -> const_iterator;\n      [[nodiscard]] auto end() const noexcept -> const_iterator;\n\n      [[nodiscard]] auto cbegin() const noexcept\n      {\n         return begin();\n      }\n\n      [[nodiscard]] auto cend() const noexcept\n      {\n         return end();\n      }\n\n      [[nodiscard]] auto is_null( const std::size_t column ) const -> bool;\n      [[nodiscard]] auto get( const std::size_t column ) const -> const char*;\n\n      template< result_type_direct T >\n      [[nodiscard]] auto get( const std::size_t column ) const -> T\n      {\n         if constexpr( requires { result_traits< T >::null(); } ) {\n            if( is_null( column ) ) {\n               return result_traits< T >::null();\n            }\n         }\n         return result_traits< T >::from( get( column ) );\n      }\n\n      template< result_type_composite T >\n      [[nodiscard]] auto get( const std::size_t column ) const -> T\n      {\n         return result_traits< T >::from( slice( column, result_traits_size< T > ) );\n      }\n\n      template< result_type T >\n      [[nodiscard]] auto optional( const std::size_t column ) const\n      {\n         return get< std::optional< T > >( column );\n      }\n\n      template< result_type T >\n      [[nodiscard]] auto as() const -> T\n      {\n         if( result_traits_size< T > != m_columns ) {\n            throw std::out_of_range( std::format( \"datatype '{}' requires {} columns, but row/slice has {} columns\", internal::demangle< T >(), result_traits_size< T >, m_columns ) );\n         }\n         return get< T >( 0 );\n      }\n\n      template< typename T >\n         requires is_aggregate_result< T >\n      [[nodiscard]] operator T() const\n      {\n         return as< T >();\n      }\n\n      template< result_type T >\n      [[nodiscard]] auto optional() const\n      {\n         return as< std::optional< T > >();\n      }\n\n      template< result_type T, result_type U >\n      [[nodiscard]] auto pair() const\n      {\n         return as< std::pair< T, U > >();\n      }\n\n      template< result_type... Ts >\n      [[nodiscard]] auto tuple() const\n      {\n         return as< std::tuple< Ts... > >();\n      }\n\n      [[nodiscard]] auto at( const std::size_t column ) const -> field;\n\n      [[nodiscard]] auto operator[]( const std::size_t column ) const noexcept -> field\n      {\n         return { *this, m_offset + column };\n      }\n\n      [[nodiscard]] auto at( const internal::zsv in_name ) const -> field\n      {\n         // row::index does the necessary checks, so we forward to operator[]\n         return ( *this )[ row::index( in_name ) ];\n      }\n\n      [[nodiscard]] auto operator[]( const internal::zsv in_name ) const -> field\n      {\n         return ( *this )[ row::index( in_name ) ];\n      }\n\n      friend void swap( row& lhs, row& rhs ) noexcept\n      {\n         std::swap( lhs.m_result, rhs.m_result );\n         std::swap( lhs.m_row, rhs.m_row );\n         std::swap( lhs.m_offset, rhs.m_offset );\n         std::swap( lhs.m_columns, rhs.m_columns );\n      }\n   };\n\n   template< result_type T >\n      requires( result_traits_size< T > == 1 )\n   auto field::as() const -> T\n   {\n      return m_row->get< T >( m_column );\n   }\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/table_field.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_TABLE_FIELD_HPP\n#define TAO_PQ_TABLE_FIELD_HPP\n\n#include <cstddef>\n#include <optional>\n#include <utility>\n\n#include <tao/pq/null.hpp>\n#include <tao/pq/result_traits.hpp>\n\nnamespace tao::pq\n{\n   class table_row;\n\n   class table_field\n   {\n   private:\n      friend class table_row;\n\n      const table_row* m_row = nullptr;\n      std::size_t m_column = 0;\n\n      table_field() = default;\n\n      table_field( const table_row& row, const std::size_t column ) noexcept\n         : m_row( &row ),\n           m_column( column )\n      {}\n\n   public:\n      [[nodiscard]] auto index() const noexcept -> std::size_t;\n\n      [[nodiscard]] auto is_null() const -> bool;\n      [[nodiscard]] auto get() const -> const char*;\n\n      template< result_type T >\n         requires( result_traits_size< T > == 1 )\n      [[nodiscard]] auto as() const -> T;  // implemented in table_row.hpp\n\n      template< result_type T >\n         requires( result_traits_size< T > == 1 )\n      [[nodiscard]] auto optional() const\n      {\n         return as< std::optional< T > >();\n      }\n\n      [[nodiscard]] auto operator==( null_t /*unused*/ ) const\n      {\n         return is_null();\n      }\n\n      friend void swap( table_field& lhs, table_field& rhs ) noexcept\n      {\n         std::swap( lhs.m_row, rhs.m_row );\n         std::swap( lhs.m_column, rhs.m_column );\n      }\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/table_reader.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_TABLE_READER_HPP\n#define TAO_PQ_TABLE_READER_HPP\n\n#include <cassert>\n#include <cstddef>\n#include <cstdint>\n#include <list>\n#include <map>\n#include <memory>\n#include <set>\n#include <string_view>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/internal/zsv.hpp>\n#include <tao/pq/table_row.hpp>\n#include <tao/pq/transaction.hpp>\n\nnamespace tao::pq\n{\n   class table_reader final\n   {\n   protected:\n      std::shared_ptr< transaction_base > m_previous;\n      std::shared_ptr< transaction > m_transaction;\n      std::size_t m_columns;  // NOLINT(modernize-use-default-member-init)\n      std::unique_ptr< char, decltype( &PQfreemem ) > m_buffer;\n      std::vector< const char* > m_data;\n\n      void check_result();\n\n   public:\n      template< parameter_type... As >\n      table_reader( const std::shared_ptr< transaction >& transaction, const internal::zsv statement, As&&... as )\n         : m_previous( transaction ),\n           m_transaction( std::make_shared< internal::transaction_guard >( transaction->connection() ) ),\n           m_columns( 0 ),\n           m_buffer( nullptr, &PQfreemem )\n      {\n         m_transaction->send( statement, std::forward< As >( as )... );\n         check_result();\n      }\n\n      ~table_reader() = default;\n\n      table_reader( const table_reader& ) = delete;\n      table_reader( table_reader&& ) = delete;\n      void operator=( const table_reader& ) = delete;\n      void operator=( table_reader&& ) = delete;\n\n      [[nodiscard]] auto columns() const noexcept -> std::size_t\n      {\n         return m_columns;\n      }\n\n      // note: the following API is experimental and subject to change\n\n      [[nodiscard]] auto get_raw_data() -> std::string_view;\n      [[nodiscard]] auto parse_data() noexcept -> bool;\n\n      [[nodiscard]] auto get_row() -> bool\n      {\n         std::ignore = get_raw_data();\n         return parse_data();\n      }\n\n      [[nodiscard]] auto has_data() const noexcept -> bool\n      {\n         return !m_data.empty();\n      }\n\n      [[nodiscard]] auto raw_data() const noexcept -> const std::vector< const char* >&\n      {\n         return m_data;\n      }\n\n      [[nodiscard]] auto row() noexcept -> table_row\n      {\n         assert( has_data() );\n         return { *this, 0, columns() };\n      }\n\n   private:\n      class const_iterator\n         : private table_row\n      {\n      private:\n         friend class table_reader;\n\n         const_iterator( const table_row& r ) noexcept\n            : table_row( r )\n         {}\n\n      public:\n         using difference_type = std::int32_t;\n         using value_type = const table_row;\n         using pointer = const table_row*;\n         using reference = const table_row&;\n\n         auto operator++() noexcept -> const_iterator&\n         {\n            if( !m_reader->get_row() ) {\n               m_columns = 0;\n            }\n            return *this;\n         }\n\n         [[nodiscard]] auto operator*() const noexcept -> const table_row&\n         {\n            return *this;\n         }\n\n         [[nodiscard]] auto operator->() const noexcept -> const table_row*\n         {\n            return this;\n         }\n\n         friend void swap( const_iterator& lhs, const_iterator& rhs ) noexcept\n         {\n            swap( static_cast< table_row& >( lhs ), static_cast< table_row& >( rhs ) );\n         }\n\n         [[nodiscard]] friend auto operator==( const const_iterator& lhs, const const_iterator& rhs ) noexcept\n         {\n            return lhs.m_columns == rhs.m_columns;\n         }\n      };\n\n   public:\n      [[nodiscard]] auto begin() -> const_iterator;\n      [[nodiscard]] auto end() noexcept -> const_iterator;\n\n      [[nodiscard]] auto cbegin()\n      {\n         return begin();\n      }\n\n      [[nodiscard]] auto cend() noexcept\n      {\n         return end();\n      }\n\n      template< typename T >\n         requires result_type< typename T::value_type >\n      [[nodiscard]] auto as_container() -> T\n      {\n         T nrv;\n         for( const auto& row : *this ) {\n            nrv.insert( nrv.end(), row.as< typename T::value_type >() );\n         }\n         return nrv;\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::vector< Ts... >::value_type >\n      [[nodiscard]] auto vector()\n      {\n         return as_container< std::vector< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::list< Ts... >::value_type >\n      [[nodiscard]] auto list()\n      {\n         return as_container< std::list< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::set< Ts... >::value_type >\n      [[nodiscard]] auto set()\n      {\n         return as_container< std::set< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::multiset< Ts... >::value_type >\n      [[nodiscard]] auto multiset()\n      {\n         return as_container< std::multiset< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::unordered_set< Ts... >::value_type >\n      [[nodiscard]] auto unordered_set()\n      {\n         return as_container< std::unordered_set< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::unordered_multiset< Ts... >::value_type >\n      [[nodiscard]] auto unordered_multiset()\n      {\n         return as_container< std::unordered_multiset< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::map< Ts... >::value_type >\n      [[nodiscard]] auto map()\n      {\n         return as_container< std::map< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::multimap< Ts... >::value_type >\n      [[nodiscard]] auto multimap()\n      {\n         return as_container< std::multimap< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::unordered_map< Ts... >::value_type >\n      [[nodiscard]] auto unordered_map()\n      {\n         return as_container< std::unordered_map< Ts... > >();\n      }\n\n      template< typename... Ts >\n         requires result_type< typename std::unordered_multimap< Ts... >::value_type >\n      [[nodiscard]] auto unordered_multimap()\n      {\n         return as_container< std::unordered_multimap< Ts... > >();\n      }\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/table_row.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_TABLE_ROW_HPP\n#define TAO_PQ_TABLE_ROW_HPP\n\n#include <cstddef>\n#include <cstdint>\n#include <format>\n#include <iterator>\n#include <optional>\n#include <stdexcept>\n#include <tuple>\n#include <utility>\n\n#include <tao/pq/internal/demangle.hpp>\n#include <tao/pq/result_traits.hpp>\n#include <tao/pq/table_field.hpp>\n\nnamespace tao::pq\n{\n   class table_reader;\n\n   class table_row\n   {\n   protected:\n      friend class table_field;\n      friend class table_reader;\n\n      table_reader* m_reader;\n      std::size_t m_offset;\n      std::size_t m_columns;\n\n      table_row( table_reader& in_reader, const std::size_t in_offset, const std::size_t in_columns ) noexcept\n         : m_reader( &in_reader ),\n           m_offset( in_offset ),\n           m_columns( in_columns )\n      {}\n\n      void ensure_column( const std::size_t column ) const;\n\n   public:\n      [[nodiscard]] auto slice( const std::size_t offset, const std::size_t in_columns ) const -> table_row;\n\n      [[nodiscard]] auto columns() const noexcept -> std::size_t\n      {\n         return m_columns;\n      }\n\n   private:\n      class const_iterator\n         : private table_field\n      {\n      private:\n         friend class table_row;\n\n         explicit const_iterator( const table_field& f ) noexcept\n            : table_field( f )\n         {}\n\n      public:\n         using difference_type = std::int32_t;\n         using value_type = const table_field;\n         using pointer = const table_field*;\n         using reference = const table_field&;\n         using iterator_category = std::random_access_iterator_tag;\n\n         const_iterator() = default;\n\n         auto operator++() noexcept -> const_iterator&\n         {\n            ++m_column;\n            return *this;\n         }\n\n         auto operator++( int ) noexcept -> const_iterator\n         {\n            const_iterator nrv( *this );\n            ++*this;\n            return nrv;\n         }\n\n         auto operator+=( const difference_type n ) noexcept -> const_iterator&\n         {\n            m_column += n;\n            return *this;\n         }\n\n         auto operator--() noexcept -> const_iterator&\n         {\n            --m_column;\n            return *this;\n         }\n\n         auto operator--( int ) noexcept -> const_iterator\n         {\n            const_iterator nrv( *this );\n            --*this;\n            return nrv;\n         }\n\n         auto operator-=( const difference_type n ) noexcept -> const_iterator&\n         {\n            m_column -= n;\n            return *this;\n         }\n\n         [[nodiscard]] auto operator*() const noexcept -> const table_field&\n         {\n            return *this;\n         }\n\n         [[nodiscard]] auto operator->() const noexcept -> const table_field*\n         {\n            return this;\n         }\n\n         [[nodiscard]] auto operator[]( const difference_type n ) const noexcept -> table_field\n         {\n            return *( *this + n );\n         }\n\n         friend void swap( const_iterator& lhs, const_iterator& rhs ) noexcept\n         {\n            swap( static_cast< table_field& >( lhs ), static_cast< table_field& >( rhs ) );\n         }\n\n         [[nodiscard]] friend auto operator+( const const_iterator& lhs, const difference_type rhs ) noexcept -> const_iterator\n         {\n            const_iterator nrv( lhs );\n            nrv += rhs;\n            return nrv;\n         }\n\n         [[nodiscard]] friend auto operator+( const difference_type lhs, const const_iterator& rhs ) noexcept -> const_iterator\n         {\n            const_iterator nrv( rhs );\n            nrv += lhs;\n            return nrv;\n         }\n\n         [[nodiscard]] friend auto operator-( const const_iterator& lhs, const difference_type rhs ) noexcept -> const_iterator\n         {\n            const_iterator nrv( lhs );\n            nrv -= rhs;\n            return nrv;\n         }\n\n         [[nodiscard]] friend auto operator-( const const_iterator& lhs, const const_iterator& rhs ) noexcept -> difference_type\n         {\n            return static_cast< difference_type >( lhs.index() ) - static_cast< difference_type >( rhs.index() );\n         }\n\n         [[nodiscard]] friend auto operator==( const const_iterator& lhs, const const_iterator& rhs ) noexcept\n         {\n            return lhs.index() == rhs.index();\n         }\n\n         [[nodiscard]] friend auto operator<=>( const const_iterator& lhs, const const_iterator& rhs ) noexcept\n         {\n            return lhs.index() <=> rhs.index();\n         }\n      };\n\n   public:\n      [[nodiscard]] auto begin() const noexcept -> const_iterator;\n      [[nodiscard]] auto end() const noexcept -> const_iterator;\n\n      [[nodiscard]] auto cbegin() const noexcept\n      {\n         return begin();\n      }\n\n      [[nodiscard]] auto cend() const noexcept\n      {\n         return end();\n      }\n\n      [[nodiscard]] auto is_null( const std::size_t column ) const -> bool;\n      [[nodiscard]] auto get( const std::size_t column ) const -> const char*;\n\n      template< result_type_direct T >\n      [[nodiscard]] auto get( const std::size_t column ) const -> T\n      {\n         const char* const value = get( column );\n         if( value == nullptr ) {\n            if constexpr( requires { result_traits< T >::null(); } ) {\n               return result_traits< T >::null();\n            }\n            else {\n               throw std::invalid_argument( \"unexpected NULL value\" );\n            }\n         }\n         return result_traits< T >::from( value );\n      }\n\n      template< result_type_composite T >\n      [[nodiscard]] auto get( const std::size_t column ) const -> T\n      {\n         return result_traits< T >::from( slice( column, result_traits_size< T > ) );\n      }\n\n      template< result_type T >\n      [[nodiscard]] auto optional( const std::size_t column ) const\n      {\n         return get< std::optional< T > >( column );\n      }\n\n      template< result_type T >\n      [[nodiscard]] auto as() const -> T\n      {\n         if( result_traits_size< T > != m_columns ) {\n            throw std::out_of_range( std::format( \"datatype '{}' requires {} columns, but table_row/slice has {} columns\", internal::demangle< T >(), result_traits_size< T >, m_columns ) );\n         }\n         return get< T >( 0 );\n      }\n\n      template< result_type T >\n      [[nodiscard]] auto optional() const\n      {\n         return as< std::optional< T > >();\n      }\n\n      template< result_type T, result_type U >\n      [[nodiscard]] auto pair() const\n      {\n         return as< std::pair< T, U > >();\n      }\n\n      template< result_type... Ts >\n      [[nodiscard]] auto tuple() const\n      {\n         return as< std::tuple< Ts... > >();\n      }\n\n      [[nodiscard]] auto at( const std::size_t column ) const -> table_field;\n\n      [[nodiscard]] auto operator[]( const std::size_t column ) const noexcept -> table_field\n      {\n         return { *this, m_offset + column };\n      }\n\n      friend void swap( table_row& lhs, table_row& rhs ) noexcept\n      {\n         std::swap( lhs.m_reader, rhs.m_reader );\n         std::swap( lhs.m_offset, rhs.m_offset );\n         std::swap( lhs.m_columns, rhs.m_columns );\n      }\n   };\n\n   template< result_type T >\n      requires( result_traits_size< T > == 1 )\n   auto table_field::as() const -> T\n   {\n      return m_row->get< T >( m_column );\n   }\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/table_writer.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_TABLE_WRITER_HPP\n#define TAO_PQ_TABLE_WRITER_HPP\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <string_view>\n#if !( defined( __cpp_pack_indexing ) && ( __cplusplus >= 202302L ) )\n#include <tuple>\n#endif\n#include <type_traits>\n#include <utility>\n\n#include <tao/pq/internal/gen.hpp>\n#include <tao/pq/internal/zsv.hpp>\n#include <tao/pq/parameter.hpp>\n#include <tao/pq/parameter_traits.hpp>\n#include <tao/pq/transaction.hpp>\n\nnamespace tao::pq\n{\n   class table_writer final\n   {\n   protected:\n      std::shared_ptr< transaction_base > m_previous;\n      std::shared_ptr< transaction > m_transaction;\n\n#if defined( __cpp_pack_indexing ) && ( __cplusplus >= 202302L )\n\n      template< std::size_t... Os, std::size_t... Is >\n      void insert_indexed( std::index_sequence< Os... > /*unused*/,\n                           std::index_sequence< Is... > /*unused*/,\n                           const auto&... ts )\n      {\n         std::string buffer;\n         ( ( ts...[ Os ].template copy_to< Is >( buffer ), buffer += '\\t' ), ... );\n         *buffer.rbegin() = '\\n';\n         table_writer::insert_raw( buffer );\n      }\n\n      template< typename... Ts >\n      void insert_traits( const Ts&... ts )\n      {\n         using gen = internal::gen< Ts::columns... >;\n         table_writer::insert_indexed( typename gen::outer_sequence(), typename gen::inner_sequence(), ts... );\n      }\n\n#else\n\n      template< std::size_t... Os, std::size_t... Is, typename... Ts >\n      void insert_indexed( std::index_sequence< Os... > /*unused*/,\n                           std::index_sequence< Is... > /*unused*/,\n                           const std::tuple< Ts... >& tuple )\n      {\n         std::string buffer;\n         ( ( std::get< Os >( tuple ).template copy_to< Is >( buffer ), buffer += '\\t' ), ... );\n         *buffer.rbegin() = '\\n';\n         table_writer::insert_raw( buffer );\n      }\n\n      template< typename... Ts >\n      void insert_traits( const Ts&... ts )\n      {\n         using gen = internal::gen< Ts::columns... >;\n         table_writer::insert_indexed( typename gen::outer_sequence(), typename gen::inner_sequence(), std::tie( ts... ) );\n      }\n\n#endif\n\n      void check_result();\n\n   public:\n      template< typename... As >\n      table_writer( const std::shared_ptr< transaction >& transaction, const internal::zsv statement, As&&... as )\n         : m_previous( transaction ),\n           m_transaction( std::make_shared< internal::transaction_guard >( transaction->connection() ) )\n      {\n         m_transaction->send( statement, std::forward< As >( as )... );\n         check_result();\n      }\n\n      ~table_writer();\n\n      table_writer( const table_writer& ) = delete;\n      table_writer( table_writer&& ) = delete;\n      void operator=( const table_writer& ) = delete;\n      void operator=( table_writer&& ) = delete;\n\n      void insert_raw( const std::string_view data );\n\n      template< parameter_type... As >\n         requires( sizeof...( As ) >= 1 )\n      void insert( As&&... as )\n      {\n         return insert_traits( parameter_traits< std::decay_t< As > >( std::forward< As >( as ) )... );\n      }\n\n      auto commit() -> std::size_t;\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/transaction.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_TRANSACTION_HPP\n#define TAO_PQ_TRANSACTION_HPP\n\n#include <chrono>\n#include <memory>\n#include <utility>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/internal/zsv.hpp>\n#include <tao/pq/parameter.hpp>\n#include <tao/pq/transaction_base.hpp>\n\nnamespace tao::pq\n{\n   class pipeline;\n\n   class transaction\n      : public transaction_base\n   {\n   protected:\n      using transaction_base::transaction_base;\n\n      [[nodiscard]] virtual auto v_is_direct() const noexcept -> bool = 0;\n\n      virtual void v_commit() = 0;\n      virtual void v_rollback() = 0;\n\n      virtual void v_reset() noexcept = 0;\n\n      void rollback_in_dtor() noexcept;\n\n   public:\n      [[nodiscard]] auto subtransaction() -> std::shared_ptr< transaction >;\n      [[nodiscard]] auto pipeline() -> std::shared_ptr< pq::pipeline >;\n\n      template< parameter_type... As >\n      auto execute( const internal::zsv statement, As&&... as )\n      {\n         const auto start = std::chrono::steady_clock::now();\n         transaction_base::send( statement, std::forward< As >( as )... );\n         return transaction_base::get_result( start );\n      }\n\n      void commit();\n      void rollback();\n   };\n\n   namespace internal\n   {\n      class subtransaction_base\n         : public transaction\n      {\n      private:\n         const std::shared_ptr< pq::transaction_base > m_previous;\n\n      protected:\n         explicit subtransaction_base( const std::shared_ptr< pq::connection >& connection )\n            : transaction( connection ),\n              m_previous( current_transaction()->shared_from_this() )\n         {\n            current_transaction() = this;\n         }\n\n         ~subtransaction_base() override\n         {\n            if( m_connection ) {\n               current_transaction() = m_previous.get();  // LCOV_EXCL_LINE\n            }\n         }\n\n         [[nodiscard]] auto v_is_direct() const noexcept -> bool final\n         {\n            return false;\n         }\n\n         void v_reset() noexcept final\n         {\n            current_transaction() = m_previous.get();\n            m_connection.reset();\n         }\n\n      public:\n         subtransaction_base( const subtransaction_base& ) = delete;\n         subtransaction_base( subtransaction_base&& ) = delete;\n         void operator=( const subtransaction_base& ) = delete;\n         void operator=( subtransaction_base&& ) = delete;\n      };\n\n      // blocker for table_reader and table_writer\n      class transaction_guard final\n         : public subtransaction_base\n      {\n      public:\n         explicit transaction_guard( const std::shared_ptr< pq::connection >& connection )\n            : subtransaction_base( connection )\n         {}\n\n      private:\n         // LCOV_EXCL_START\n         void v_commit() override {}\n         void v_rollback() override {}\n         // LCOV_EXCL_STOP\n      };\n\n   }  // namespace internal\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/transaction_base.hpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_TRANSACTION_BASE_HPP\n#define TAO_PQ_TRANSACTION_BASE_HPP\n\n#include <chrono>\n#include <cstddef>\n#include <memory>\n#if !( defined( __cpp_pack_indexing ) && ( __cplusplus >= 202302L ) )\n#include <tuple>\n#endif\n#include <type_traits>\n#include <utility>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/internal/gen.hpp>\n#include <tao/pq/internal/zsv.hpp>\n#include <tao/pq/parameter.hpp>\n#include <tao/pq/parameter_traits.hpp>\n#include <tao/pq/result.hpp>\n\nnamespace tao::pq\n{\n   class connection;\n   class table_reader;\n   class table_writer;\n\n   class transaction_base\n      : public std::enable_shared_from_this< transaction_base >\n   {\n   protected:\n      std::shared_ptr< pq::connection > m_connection;\n\n      friend class table_reader;\n      friend class table_writer;\n\n      explicit transaction_base( const std::shared_ptr< pq::connection >& connection ) noexcept;\n\n   public:\n      virtual ~transaction_base() = default;\n\n      transaction_base( const transaction_base& ) = delete;\n      transaction_base( transaction_base&& ) = delete;\n      void operator=( const transaction_base& ) = delete;\n      void operator=( transaction_base&& ) = delete;\n\n   protected:\n      [[nodiscard]] auto current_transaction() const noexcept -> transaction_base*&;\n      void check_current_transaction() const;\n\n      void send_params( const char* statement,\n                        const int n_params,\n                        const Oid types[],\n                        const char* const values[],\n                        const int lengths[],\n                        const int formats[] );\n\n#if defined( __cpp_pack_indexing ) && ( __cplusplus >= 202302L )\n\n      template< std::size_t... Os, std::size_t... Is >\n      void send_indexed( const char* statement,\n                         std::index_sequence< Os... > /*unused*/,\n                         std::index_sequence< Is... > /*unused*/,\n                         const auto&... ts )\n      {\n         const Oid types[] = { static_cast< Oid >( ts...[ Os ].template type< Is >() )... };\n         const char* const values[] = { ts...[ Os ].template value< Is >()... };\n         const int lengths[] = { ts...[ Os ].template length< Is >()... };\n         const int formats[] = { ts...[ Os ].template format< Is >()... };\n         send_params( statement, sizeof...( Os ), types, values, lengths, formats );\n      }\n\n      template< typename... Ts >\n      void send_traits( const char* statement, const Ts&... ts )\n      {\n         using gen = internal::gen< Ts::columns... >;\n         transaction_base::send_indexed( statement, typename gen::outer_sequence(), typename gen::inner_sequence(), ts... );\n      }\n\n#else\n\n      template< std::size_t... Os, std::size_t... Is, typename... Ts >\n      void send_indexed( const char* statement,\n                         std::index_sequence< Os... > /*unused*/,\n                         std::index_sequence< Is... > /*unused*/,\n                         const std::tuple< Ts... >& tuple )\n      {\n         const Oid types[] = { static_cast< Oid >( std::get< Os >( tuple ).template type< Is >() )... };\n         const char* const values[] = { std::get< Os >( tuple ).template value< Is >()... };\n         const int lengths[] = { std::get< Os >( tuple ).template length< Is >()... };\n         const int formats[] = { std::get< Os >( tuple ).template format< Is >()... };\n         send_params( statement, sizeof...( Os ), types, values, lengths, formats );\n      }\n\n      template< typename... Ts >\n      void send_traits( const char* statement, const Ts&... ts )\n      {\n         using gen = internal::gen< Ts::columns... >;\n         transaction_base::send_indexed( statement, typename gen::outer_sequence(), typename gen::inner_sequence(), std::tie( ts... ) );\n      }\n\n#endif\n\n   public:\n      [[nodiscard]] auto connection() const noexcept -> const std::shared_ptr< pq::connection >&\n      {\n         return m_connection;\n      }\n\n      void send( const internal::zsv statement )\n      {\n         send_params( statement, 0, nullptr, nullptr, nullptr, nullptr );\n      }\n\n      template< parameter_type_direct... As >\n      void send( const internal::zsv statement, As&&... as )\n      {\n         send_traits( statement, parameter_traits< std::decay_t< As > >( std::forward< As >( as ) )... );\n      }\n\n      template< parameter_type... As >\n         requires( parameter_type_dynamic< As > || ... )\n      void send( const internal::zsv statement, As&&... as )\n      {\n         const parameter< internal::parameter_size< As... > > p( std::forward< As >( as )... );\n         send_params( statement, p.m_size, p.m_types, p.m_values, p.m_lengths, p.m_formats );\n      }\n\n      template< parameter_type_dynamic A >\n      void send( const internal::zsv statement, A&& p )\n      {\n         send_params( statement, p.m_size, p.m_types, p.m_values, p.m_lengths, p.m_formats );\n      }\n\n      void set_single_row_mode();\n#if defined( LIBPQ_HAS_CHUNK_MODE )\n      void set_chunk_mode( const int rows );\n#endif\n\n      [[nodiscard]] auto get_result( const std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now() ) -> result;\n      void consume_pipeline_sync( const std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now() );\n   };\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/transaction_status.hpp",
    "content": "// Copyright (c) 2022-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_TRANSACTION_STATUS_HPP\n#define TAO_PQ_TRANSACTION_STATUS_HPP\n\n#include <cstdint>\n#include <string_view>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/internal/format_as.hpp>\n\nnamespace tao::pq\n{\n   enum class transaction_status : std::uint8_t\n   {\n      idle = PQTRANS_IDLE,\n      in_transaction = PQTRANS_INTRANS,\n      active = PQTRANS_ACTIVE,\n      error = PQTRANS_INERROR,\n      unknown = PQTRANS_UNKNOWN\n   };\n\n   [[nodiscard]] constexpr auto taopq_format_as( const transaction_status ts ) noexcept -> std::string_view\n   {\n      switch( ts ) {\n         case transaction_status::idle:\n            return \"idle\";\n\n         case transaction_status::in_transaction:\n            return \"in_transaction\";\n\n         case transaction_status::active:\n            return \"active\";\n\n         case transaction_status::error:\n            return \"error\";\n\n         case transaction_status::unknown:\n            return \"unknown\";\n\n         default:\n            return \"<unknown>\";\n      }\n   }\n\n}  // namespace tao::pq\n\n#endif\n"
  },
  {
    "path": "include/tao/pq/version.hpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_VERSION_HPP\n#define TAO_PQ_VERSION_HPP\n\n#define TAO_PQ_VERSION \"0.9.0\"\n\n#define TAO_PQ_VERSION_MAJOR 0\n#define TAO_PQ_VERSION_MINOR 9\n#define TAO_PQ_VERSION_PATCH 0\n\n#endif\n"
  },
  {
    "path": "include/tao/pq.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef TAO_PQ_HPP\n#define TAO_PQ_HPP\n\n// NOLINTBEGIN(misc-include-cleaner)\n\n#include <tao/pq/version.hpp>\n\n#include <tao/pq/binary.hpp>\n#include <tao/pq/null.hpp>\n#include <tao/pq/oid.hpp>\n\n#include <tao/pq/connection.hpp>\n#include <tao/pq/connection_pool.hpp>\n#include <tao/pq/pipeline.hpp>\n#include <tao/pq/transaction.hpp>\n\n#include <tao/pq/parameter.hpp>\n#include <tao/pq/parameter_traits.hpp>\n#include <tao/pq/parameter_traits_aggregate.hpp>\n#include <tao/pq/parameter_traits_array.hpp>\n#include <tao/pq/parameter_traits_optional.hpp>\n#include <tao/pq/parameter_traits_pair.hpp>\n#include <tao/pq/parameter_traits_tuple.hpp>\n\n#include <tao/pq/exception.hpp>\n#include <tao/pq/log.hpp>\n#include <tao/pq/result.hpp>\n\n#include <tao/pq/result_traits.hpp>\n#include <tao/pq/result_traits_aggregate.hpp>\n#include <tao/pq/result_traits_array.hpp>\n#include <tao/pq/result_traits_optional.hpp>\n#include <tao/pq/result_traits_pair.hpp>\n#include <tao/pq/result_traits_tuple.hpp>\n\n#include <tao/pq/table_reader.hpp>\n#include <tao/pq/table_writer.hpp>\n\n#include <tao/pq/large_object.hpp>\n\n// NOLINTEND(misc-include-cleaner)\n\n#endif\n"
  },
  {
    "path": "src/lib/pq/connection.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/connection.hpp>\n\n#include <algorithm>\n#include <cctype>\n#include <chrono>\n#include <cstring>\n#include <format>\n#include <functional>\n#include <memory>\n#include <optional>\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <utility>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/access_mode.hpp>\n#include <tao/pq/connection_status.hpp>\n#include <tao/pq/exception.hpp>\n#include <tao/pq/internal/poll.hpp>\n#include <tao/pq/internal/unreachable.hpp>\n#include <tao/pq/isolation_level.hpp>\n#include <tao/pq/notification.hpp>\n#include <tao/pq/oid.hpp>\n#include <tao/pq/poll.hpp>\n#include <tao/pq/result.hpp>\n#include <tao/pq/transaction_status.hpp>\n\nnamespace tao::pq\n{\n   namespace internal\n   {\n      class transaction_base\n         : public transaction\n      {\n      protected:\n         explicit transaction_base( const std::shared_ptr< pq::connection >& connection )\n            : transaction( connection )\n         {\n            if( current_transaction() != nullptr ) {\n               throw std::logic_error( \"invalid transaction order\" );\n            }\n            current_transaction() = this;\n         }\n\n         ~transaction_base() override\n         {\n            if( m_connection ) {\n               current_transaction() = nullptr;\n            }\n         }\n\n         void v_reset() noexcept final\n         {\n            current_transaction() = nullptr;\n            m_connection.reset();\n         }\n\n      public:\n         transaction_base( const transaction_base& ) = delete;\n         transaction_base( transaction_base&& ) = delete;\n         void operator=( const transaction_base& ) = delete;\n         void operator=( transaction_base&& ) = delete;\n      };\n\n      class autocommit_transaction final\n         : public transaction_base\n      {\n      public:\n         explicit autocommit_transaction( const std::shared_ptr< pq::connection >& connection )\n            : transaction_base( connection )\n         {}\n\n      private:\n         [[nodiscard]] auto v_is_direct() const noexcept -> bool override\n         {\n            return true;\n         }\n\n         void v_commit() override\n         {}\n\n         void v_rollback() override\n         {}\n      };\n\n      namespace\n      {\n         [[nodiscard]] inline auto isolation_level_extension( const isolation_level il ) -> const char*\n         {\n            switch( il ) {\n               case isolation_level::default_isolation_level:\n                  return \"\";\n               case isolation_level::serializable:\n                  return \" ISOLATION LEVEL SERIALIZABLE\";\n               case isolation_level::repeatable_read:\n                  return \" ISOLATION LEVEL REPEATABLE READ\";\n               case isolation_level::read_committed:\n                  return \" ISOLATION LEVEL READ COMMITTED\";\n               case isolation_level::read_uncommitted:\n                  return \" ISOLATION LEVEL READ UNCOMMITTED\";\n            }\n            TAO_PQ_INTERNAL_UNREACHABLE;  // LCOV_EXCL_LINE\n         }\n\n         [[nodiscard]] inline auto access_mode_extension( const access_mode am ) -> const char*\n         {\n            switch( am ) {\n               case access_mode::default_access_mode:\n                  return \"\";\n               case access_mode::read_write:\n                  return \" READ WRITE\";\n               case access_mode::read_only:\n                  return \" READ ONLY\";\n            }\n            TAO_PQ_INTERNAL_UNREACHABLE;  // LCOV_EXCL_LINE\n         }\n\n      }  // namespace\n\n      class top_level_transaction final\n         : public transaction_base\n      {\n      public:\n         top_level_transaction( const std::shared_ptr< pq::connection >& connection, const isolation_level il, const access_mode am )\n            : transaction_base( connection )\n         {\n            this->execute( std::format( \"START TRANSACTION{}{}\", isolation_level_extension( il ), access_mode_extension( am ) ) );\n         }\n\n         ~top_level_transaction() override\n         {\n            if( m_connection && m_connection->attempt_rollback() ) {\n               rollback_in_dtor();\n            }\n         }\n\n         top_level_transaction( const top_level_transaction& ) = delete;\n         top_level_transaction( top_level_transaction&& ) = delete;\n         void operator=( const top_level_transaction& ) = delete;\n         void operator=( top_level_transaction&& ) = delete;\n\n      private:\n         [[nodiscard]] auto v_is_direct() const noexcept -> bool override\n         {\n            return false;\n         }\n\n         void v_commit() override\n         {\n            execute( \"COMMIT TRANSACTION\" );\n         }\n\n         void v_rollback() override\n         {\n            execute( \"ROLLBACK TRANSACTION\" );\n         }\n      };\n\n      namespace\n      {\n         [[nodiscard]] constexpr auto is_identifier( const std::string_view value ) noexcept -> bool\n         {\n            return !value.empty() && ( std::isdigit( static_cast< unsigned char >( value[ 0 ] ) ) == 0 ) && ( value.find_first_not_of( \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_\" ) == std::string_view::npos );\n         }\n\n      }  // namespace\n\n   }  // namespace internal\n\n   auto connection::escape_identifier( const std::string_view identifier ) const -> std::unique_ptr< char, decltype( &PQfreemem ) >\n   {\n      std::unique_ptr< char, decltype( &PQfreemem ) > buffer( PQescapeIdentifier( m_pgconn.get(), identifier.data(), identifier.size() ), &PQfreemem );\n      if( !buffer ) {\n         throw std::invalid_argument( error_message() );  // LCOV_EXCL_LINE\n      }\n      return buffer;\n   }\n\n   auto connection::attempt_rollback() const noexcept -> bool\n   {\n      switch( transaction_status() ) {\n         // LCOV_EXCL_START\n         case transaction_status::idle:\n         case transaction_status::active:\n            return false;\n            // LCOV_EXCL_STOP\n\n         case transaction_status::in_transaction:\n         case transaction_status::error:\n         case transaction_status::unknown:\n            return true;\n      }\n      TAO_PQ_INTERNAL_UNREACHABLE;  // LCOV_EXCL_LINE\n   }\n\n   void connection::check_prepared_name( const std::string_view name )\n   {\n      if( !internal::is_identifier( name ) ) {\n         throw std::invalid_argument( std::format( \"invalid prepared statement name: {}\", name ) );\n      }\n   }\n\n   void connection::send_params( const char* statement,\n                                 const int n_params,\n                                 const Oid types[],\n                                 const char* const values[],\n                                 const int lengths[],\n                                 const int formats[] )\n   {\n      const auto is_prepared = m_prepared_statements.contains( statement );\n      if( m_log ) {\n         if( is_prepared ) {\n            if( m_log->connection.send_query_prepared ) {\n               m_log->connection.send_query_prepared( *this, statement, n_params, values, lengths, formats );\n            }\n         }\n         else {\n            if( m_log->connection.send_query ) {\n               m_log->connection.send_query( *this, statement, n_params, types, values, lengths, formats );\n            }\n         }\n      }\n      const auto result = is_prepared ?\n                             PQsendQueryPrepared( m_pgconn.get(), statement, n_params, values, lengths, formats, 0 ) :\n                             PQsendQueryParams( m_pgconn.get(), statement, n_params, types, values, lengths, formats, 0 );\n      if( m_log ) {\n         if( is_prepared ) {\n            if( m_log->connection.send_query_prepared.result ) {\n               m_log->connection.send_query_prepared.result( *this, result );\n            }\n         }\n         else {\n            if( m_log->connection.send_query.result ) {\n               m_log->connection.send_query.result( *this, result );\n            }\n         }\n      }\n      if( result == 0 ) {\n         throw pq::connection_error( error_message() );  // LCOV_EXCL_LINE\n      }\n   }\n\n   void connection::wait( const bool wait_for_write, const std::chrono::steady_clock::time_point end )\n   {\n      if( m_log && m_log->connection.wait ) {\n         m_log->connection.wait( *this, wait_for_write, end );\n      }\n      while( true ) {\n         int timeout_ms = -1;\n         if( m_timeout ) {\n            timeout_ms = std::max( static_cast< int >( std::chrono::duration_cast< std::chrono::milliseconds >( end - std::chrono::steady_clock::now() ).count() ), 0 );\n         }\n\n         const auto so = socket();\n         if( m_log && m_log->connection.poll ) {\n            m_log->connection.poll( *this, so, wait_for_write, timeout_ms );\n         }\n         const auto status = m_poll( so, wait_for_write, timeout_ms );\n         if( m_log && m_log->connection.poll.result ) {\n            m_log->connection.poll.result( *this, so, status );\n         }\n         switch( status ) {\n            case poll::status::timeout:\n               m_pgconn.reset();\n               throw timeout_reached( \"timeout reached\" );\n\n            case poll::status::readable:\n               get_notifications();\n               return;\n\n               // LCOV_EXCL_START\n            case poll::status::writable:\n               return;\n\n            case poll::status::again:\n               break;\n\n            default:\n               TAO_PQ_INTERNAL_UNREACHABLE;\n               // LCOV_EXCL_STOP\n         }\n      }\n   }\n\n   void connection::cancel()\n   {\n      const std::unique_ptr< PGcancel, decltype( &PQfreeCancel ) > p( PQgetCancel( m_pgconn.get() ), &PQfreeCancel );\n      if( p ) {\n         char buffer[ 256 ];\n         if( PQcancel( p.get(), buffer, sizeof( buffer ) ) == 0 ) {\n            throw pq::error( buffer );  // LCOV_EXCL_LINE\n         }\n      }\n   }\n\n   auto connection::get_result( const std::chrono::steady_clock::time_point end ) -> std::unique_ptr< PGresult, decltype( &PQclear ) >\n   {\n      if( m_log && m_log->connection.get_result ) {\n         m_log->connection.get_result( *this, end );\n      }\n      bool wait_for_write = true;\n      while( is_busy() ) {\n         if( wait_for_write ) {\n            wait_for_write = flush();\n         }\n         connection::wait( wait_for_write, end );\n      }\n\n      std::unique_ptr< PGresult, decltype( &PQclear ) > result( PQgetResult( m_pgconn.get() ), &PQclear );\n      if( m_log && m_log->connection.get_result.result ) {\n         m_log->connection.get_result.result( *this, result.get() );\n      }\n      handle_notifications();\n      return result;\n   }\n\n   auto connection::get_fatal_error( const std::chrono::steady_clock::time_point end ) -> std::unique_ptr< PGresult, decltype( &PQclear ) >\n   {\n      auto result = connection::get_result( end );\n      if( !result ) {\n         throw std::runtime_error( \"unable to obtain result\" );\n      }\n\n      const auto status = PQresultStatus( result.get() );\n      if( status != PGRES_FATAL_ERROR ) {\n         throw std::runtime_error( std::format( \"unexpected result status: {}\", PQresStatus( status ) ) );\n      }\n\n      return result;\n   }\n\n   void connection::consume_empty_result( const std::chrono::steady_clock::time_point end )\n   {\n      if( const auto result = connection::get_result( end ) ) {\n         const auto status = PQresultStatus( result.get() );\n         throw std::runtime_error( std::format( \"unexpected result status: {}\", PQresStatus( status ) ) );\n      }\n   }\n\n   auto connection::get_copy_data( char*& buffer, const std::chrono::steady_clock::time_point end ) -> std::size_t\n   {\n      while( true ) {\n         const auto result = PQgetCopyData( m_pgconn.get(), &buffer, 1 );\n         if( result > 0 ) {\n            return static_cast< std::size_t >( result );\n         }\n         switch( result ) {\n            case 0:\n               connection::wait( false, end );\n               break;\n\n            case -1:\n               return 0;\n\n               // LCOV_EXCL_START\n            case -2:\n               throw pq::error( std::format( \"PQgetCopyData() failed: {}\", error_message() ) );\n\n            default:\n               TAO_PQ_INTERNAL_UNREACHABLE;\n               // LCOV_EXCL_STOP\n         }\n      }\n   }\n\n   auto connection::get_copy_data( char*& buffer ) -> std::size_t\n   {\n      return connection::get_copy_data( buffer, timeout_end() );\n   }\n\n   void connection::put_copy_data( const char* buffer, const std::size_t size )\n   {\n      const auto end = timeout_end();\n      while( true ) {\n         switch( PQputCopyData( m_pgconn.get(), buffer, static_cast< int >( size ) ) ) {\n            case 1:\n               return;\n\n               // LCOV_EXCL_START\n            case 0:\n               connection::wait( true, end );\n               break;\n\n            case -1:\n               throw pq::error( std::format( \"PQputCopyData() failed: {}\", error_message() ) );\n\n            default:\n               TAO_PQ_INTERNAL_UNREACHABLE;\n               // LCOV_EXCL_STOP\n         }\n      }\n   }\n\n   void connection::put_copy_end( const char* error_message )\n   {\n      const auto end = timeout_end();\n      while( true ) {\n         switch( PQputCopyEnd( m_pgconn.get(), error_message ) ) {\n            case 1:\n               return;\n\n               // LCOV_EXCL_START\n            case 0:\n               connection::wait( true, end );\n               break;\n\n            case -1:\n               throw pq::error( std::format( \"PQputCopyEnd() failed: {}\", connection::error_message() ) );\n\n            default:\n               TAO_PQ_INTERNAL_UNREACHABLE;\n               // LCOV_EXCL_STOP\n         }\n      }\n   }\n\n   void connection::clear_copy_data( const std::chrono::steady_clock::time_point end )\n   {\n      char* ptr;\n      while( connection::get_copy_data( ptr, end ) > 0 ) {\n         PQfreemem( ptr );\n      }\n   }\n\n   connection::connection( const private_key /*unused*/, const std::string& connection_info )\n      : m_pgconn( PQconnectdb( connection_info.c_str() ), &PQfinish ),\n        m_current_transaction( nullptr ),\n        m_poll( internal::poll )\n   {\n      if( !is_open() ) {\n         // note that we can not access the sqlstate after PQconnectdb(),\n         // see https://stackoverflow.com/q/23349086/2073257\n         throw pq::connection_error( error_message() );\n      }\n\n      if( PQsetnonblocking( m_pgconn.get(), 1 ) != 0 ) {\n         throw pq::connection_error( error_message() );  // LCOV_EXCL_LINE\n      }\n   }\n\n   auto connection::create( const std::string& connection_info ) -> std::shared_ptr< connection >\n   {\n      return std::make_shared< connection >( private_key(), connection_info );\n   }\n\n   auto connection::error_message() const -> const char*\n   {\n      return PQerrorMessage( m_pgconn.get() );\n   }\n\n   auto connection::notification_handler( const std::string_view channel ) const -> std::function< void( const char* payload ) >\n   {\n      const auto it = m_notification_handlers.find( channel );\n      if( it != m_notification_handlers.end() ) {\n         return it->second;\n      }\n      return {};\n   }\n\n   void connection::set_notification_handler( const std::string_view channel, const std::function< void( const char* payload ) >& handler )\n   {\n      m_notification_handlers[ std::string( channel ) ] = handler;\n   }\n\n   void connection::reset_notification_handler( const std::string_view channel ) noexcept\n   {\n      const auto it = m_notification_handlers.find( channel );\n      if( it != m_notification_handlers.end() ) {\n         m_notification_handlers.erase( it );\n      }\n   }\n\n   auto connection::status() const noexcept -> connection_status\n   {\n      return static_cast< connection_status >( PQstatus( m_pgconn.get() ) );\n   }\n\n   auto connection::transaction_status() const noexcept -> pq::transaction_status\n   {\n      return static_cast< pq::transaction_status >( PQtransactionStatus( m_pgconn.get() ) );\n   }\n\n   auto connection::pipeline_status() const noexcept -> pq::pipeline_status\n   {\n      return static_cast< pq::pipeline_status >( PQpipelineStatus( m_pgconn.get() ) );\n   }\n\n   void connection::enter_pipeline_mode()\n   {\n      const auto result = PQenterPipelineMode( m_pgconn.get() );\n      if( m_log && m_log->connection.enter_pipeline_mode.result ) {\n         m_log->connection.enter_pipeline_mode.result( *this, result );\n      }\n      if( result == 0 ) {\n         throw pq::connection_error( \"unable to enter pipeline mode\" );\n      }\n   }\n\n   void connection::exit_pipeline_mode()\n   {\n      if( m_log && m_log->connection.exit_pipeline_mode ) {\n         m_log->connection.exit_pipeline_mode( *this );\n      }\n      const auto result = PQexitPipelineMode( m_pgconn.get() );\n      if( m_log && m_log->connection.exit_pipeline_mode.result ) {\n         m_log->connection.exit_pipeline_mode.result( *this, result );\n      }\n      if( result == 0 ) {\n         throw pq::connection_error( error_message() );\n      }\n   }\n\n   void connection::pipeline_sync()\n   {\n      if( m_log && m_log->connection.pipeline_sync ) {\n         m_log->connection.pipeline_sync( *this );\n      }\n      const auto result = PQpipelineSync( m_pgconn.get() );\n      if( m_log && m_log->connection.pipeline_sync.result ) {\n         m_log->connection.pipeline_sync.result( *this, result );\n      }\n      if( result == 0 ) {\n         throw pq::connection_error( \"unable to sync pipeline\" );\n      }\n   }\n\n   auto connection::is_busy() const noexcept -> bool\n   {\n      const auto result = PQisBusy( m_pgconn.get() );\n      if( m_log && m_log->connection.is_busy.result ) {\n         m_log->connection.is_busy.result( *this, result );\n      }\n      return result != 0;\n   }\n\n   auto connection::flush() -> bool\n   {\n      if( m_log && m_log->connection.flush ) {\n         m_log->connection.flush( *this );\n      }\n      const auto result = PQflush( m_pgconn.get() );\n      if( m_log && m_log->connection.flush.result ) {\n         m_log->connection.flush.result( *this, result );\n      }\n      switch( result ) {\n         case 0:\n            return false;\n\n         case 1:\n            return true;\n\n         default:\n            throw pq::error( std::format( \"PQflush() failed: {}\", error_message() ) );\n      }\n   }\n\n   void connection::consume_input()\n   {\n      if( m_log && m_log->connection.consume_input ) {\n         m_log->connection.consume_input( *this );\n      }\n      const auto result = PQconsumeInput( m_pgconn.get() );\n      if( m_log && m_log->connection.consume_input.result ) {\n         m_log->connection.consume_input.result( *this, result );\n      }\n      if( result == 0 ) {\n         throw pq::connection_error( error_message() );\n      }\n   }\n\n   auto connection::direct() -> std::shared_ptr< pq::transaction >\n   {\n      return std::make_shared< internal::autocommit_transaction >( shared_from_this() );\n   }\n\n   auto connection::transaction() -> std::shared_ptr< pq::transaction >\n   {\n      return std::make_shared< internal::top_level_transaction >( shared_from_this(), isolation_level::default_isolation_level, access_mode::default_access_mode );\n   }\n\n   auto connection::transaction( const access_mode am, const isolation_level il ) -> std::shared_ptr< pq::transaction >\n   {\n      return std::make_shared< internal::top_level_transaction >( shared_from_this(), il, am );\n   }\n\n   auto connection::transaction( const isolation_level il, const access_mode am ) -> std::shared_ptr< pq::transaction >\n   {\n      return std::make_shared< internal::top_level_transaction >( shared_from_this(), il, am );\n   }\n\n   auto connection::pipeline() -> std::shared_ptr< pq::pipeline >\n   {\n      return direct()->pipeline();\n   }\n\n   void connection::prepare( std::string name, const internal::zsv statement )\n   {\n      connection::check_prepared_name( name );\n      const auto end = timeout_end();\n\n      if( PQsendPrepare( m_pgconn.get(), name.c_str(), statement, 0, nullptr ) == 0 ) {\n         throw pq::connection_error( error_message() );  // LCOV_EXCL_LINE\n      }\n\n      const auto result = connection::get_result( end );\n      switch( PQresultStatus( result.get() ) ) {\n         case PGRES_COMMAND_OK:\n            connection::consume_empty_result( end );\n            break;\n\n         case PGRES_TUPLES_OK:\n         case PGRES_EMPTY_QUERY:\n         case PGRES_COPY_IN:\n         case PGRES_COPY_OUT:\n            TAO_PQ_INTERNAL_UNREACHABLE;  // LCOV_EXCL_LINE\n\n         default:\n            connection::consume_empty_result( end );\n            internal::throw_sqlstate( result.get() );\n      }\n\n      m_prepared_statements.insert( std::move( name ) );\n   }\n\n   void connection::deallocate( const std::string_view name )\n   {\n      connection::check_prepared_name( name );\n      const auto it = m_prepared_statements.find( name );\n      if( it == m_prepared_statements.end() ) {\n         throw std::runtime_error( std::format( \"prepared statement not found: {}\", name ) );\n      }\n      connection::execute( std::format( \"DEALLOCATE {}\", connection::escape_identifier( name ).get() ) );\n      m_prepared_statements.erase( it );\n   }\n\n   void connection::listen( const std::string_view channel )\n   {\n      connection::execute( std::format( \"LISTEN {}\", connection::escape_identifier( channel ).get() ) );\n   }\n\n   void connection::listen( const std::string_view channel, const std::function< void( const char* payload ) >& handler )\n   {\n      connection::set_notification_handler( channel, handler );\n      connection::listen( channel );\n   }\n\n   void connection::unlisten( const std::string_view channel )\n   {\n      connection::execute( std::format( \"UNLISTEN {}\", connection::escape_identifier( channel ).get() ) );\n   }\n\n   void connection::notify( const std::string_view channel )\n   {\n      connection::execute( std::format( \"NOTIFY {}\", connection::escape_identifier( channel ).get() ) );\n   }\n\n   void connection::notify( const std::string_view channel, const std::string_view payload )\n   {\n      connection::execute( \"SELECT pg_notify( $1, $2 )\", channel, payload );\n   }\n\n   void connection::handle_notifications()\n   {\n      while( PGnotify* pgnotify = PQnotifies( m_pgconn.get() ) ) {\n         const notification notify( pgnotify );\n         if( m_notification_handler ) {\n            m_notification_handler( notify );\n         }\n         const auto it = m_notification_handlers.find( notify.channel() );\n         if( it != m_notification_handlers.end() ) {\n            it->second( notify.payload() );\n         }\n      }\n   }\n\n   void connection::get_notifications()\n   {\n      consume_input();\n      handle_notifications();\n   }\n\n   auto connection::socket() const -> int\n   {\n      const auto fd = PQsocket( m_pgconn.get() );\n      if( fd < 0 ) {\n         throw pq::error( \"PQsocket(): unable to retrieve file descriptor\" );  // LCOV_EXCL_LINE\n      }\n      return fd;\n   }\n\n   auto connection::password( const internal::zsv passwd, const internal::zsv user, const internal::zsv algorithm ) -> std::string\n   {\n      const std::unique_ptr< char, decltype( &PQfreemem ) > buffer( PQencryptPasswordConn( m_pgconn.get(), passwd, user, algorithm ), &PQfreemem );\n      if( !buffer ) {\n         throw std::invalid_argument( error_message() );  // LCOV_EXCL_LINE\n      }\n      return buffer.get();\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/connection_pool.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/connection_pool.hpp>\n\n#include <memory>\n#include <string_view>\n\n#include <tao/pq/connection.hpp>\n#include <tao/pq/internal/poll.hpp>\n\nnamespace tao::pq\n{\n   auto connection_pool::v_create() const -> std::unique_ptr< pq::connection >\n   {\n      return std::make_unique< pq::connection >( pq::connection::private_key(), m_connection_info );\n   }\n\n   connection_pool::connection_pool( const private_key /*unused*/, const std::string_view connection_info )\n      : m_connection_info( connection_info ),\n        m_poll( internal::poll )\n   {}\n\n   auto connection_pool::connection() -> std::shared_ptr< pq::connection >\n   {\n      auto result = internal::pool< pq::connection >::get();\n      if( m_timeout ) {\n         result->set_timeout( *m_timeout );\n      }\n      else {\n         result->reset_timeout();\n      }\n      result->set_poll_callback( m_poll );\n      return result;\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/exception.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/exception.hpp>\n\n#include <stdexcept>\n#include <string_view>\n\n#include <libpq-fe.h>\n\nnamespace tao::pq\n{\n   sql_error::sql_error( const char* what, const std::string_view in_sqlstate )\n      : error( what ),\n        sqlstate( in_sqlstate )\n   {}\n\n   connection_error::connection_error( const char* what )\n      : connection_error( what, \"08000\" )\n   {}\n\n   namespace internal\n   {\n      void throw_sqlstate( PGresult* pgresult )\n      {\n         const char* error_message = PQresultErrorMessage( pgresult );\n         const char* sql_state = PQresultErrorField( pgresult, PG_DIAG_SQLSTATE );\n         if( sql_state == nullptr ) {\n            throw std::runtime_error( error_message );\n         }\n         internal::throw_sqlstate( error_message, sql_state );\n      }\n\n      void throw_sqlstate( const char* error_message, const std::string_view sql_state )\n      {\n         // LCOV_EXCL_START\n         switch( sql_state[ 0 ] ) {\n            case '0':\n               switch( sql_state[ 1 ] ) {\n                  case '0':\n                     throw success( error_message, sql_state );\n\n                  case '1':\n                     if( sql_state == \"01003\" ) {\n                        throw null_value_eliminated_in_set_function( error_message, sql_state );\n                     }\n                     if( sql_state == \"01004\" ) {\n                        throw string_data_right_truncation< warning >( error_message, sql_state );\n                     }\n                     if( sql_state == \"01006\" ) {\n                        throw privilege_not_revoked( error_message, sql_state );\n                     }\n                     if( sql_state == \"01007\" ) {\n                        throw privilege_not_granted( error_message, sql_state );\n                     }\n                     if( sql_state == \"01008\" ) {\n                        throw implicit_zero_bit_padding( error_message, sql_state );\n                     }\n                     if( sql_state == \"0100C\" ) {\n                        throw dynamic_result_sets_returned( error_message, sql_state );\n                     }\n                     if( sql_state == \"01P01\" ) {\n                        throw deprecated_feature( error_message, sql_state );\n                     }\n                     throw warning( error_message, sql_state );\n\n                  case '2':\n                     if( sql_state == \"02001\" ) {\n                        throw no_additional_dynamic_result_sets_returned( error_message, sql_state );\n                     }\n                     throw no_data( error_message, sql_state );\n\n                  case '3':\n                     throw sql_statement_not_yet_complete( error_message, sql_state );\n\n                  case '8':\n                     if( sql_state == \"08001\" ) {\n                        throw sqlclient_unable_to_establish_sqlconnection( error_message, sql_state );\n                     }\n                     if( sql_state == \"08003\" ) {\n                        throw connection_does_not_exist( error_message, sql_state );\n                     }\n                     if( sql_state == \"08004\" ) {\n                        throw sqlserver_rejected_establishment_of_sqlconnection( error_message, sql_state );\n                     }\n                     if( sql_state == \"08006\" ) {\n                        throw connection_failure( error_message, sql_state );\n                     }\n                     if( sql_state == \"08007\" ) {\n                        throw transaction_resolution_unknown( error_message, sql_state );\n                     }\n                     if( sql_state == \"08P01\" ) {\n                        throw protocol_violation( error_message, sql_state );\n                     }\n                     throw connection_error( error_message, sql_state );\n\n                  case '9':\n                     throw triggered_action_exception( error_message, sql_state );\n\n                  case 'A':\n                     throw feature_not_supported( error_message, sql_state );\n\n                  case 'B':\n                     throw invalid_transaction_initiation( error_message, sql_state );\n\n                  case 'F':\n                     if( sql_state == \"0F001\" ) {\n                        throw invalid_locator_specification( error_message, sql_state );\n                     }\n                     throw locator_exception( error_message, sql_state );\n\n                  case 'L':\n                     if( sql_state == \"0LP01\" ) {\n                        throw invalid_grant_operation( error_message, sql_state );\n                     }\n                     throw invalid_grantor( error_message, sql_state );\n\n                  case 'P':\n                     throw invalid_role_specification( error_message, sql_state );\n\n                  case 'Z':\n                     if( sql_state == \"0Z002\" ) {\n                        throw stacked_diagnostics_accessed_without_active_handler( error_message, sql_state );\n                     }\n                     throw diagnostics_exception( error_message, sql_state );\n\n                  default:\n                     throw sql_error( error_message, sql_state );\n               }\n\n            case '2':\n               switch( sql_state[ 1 ] ) {\n                  case '0':\n                     throw case_not_found( error_message, sql_state );\n\n                  case '1':\n                     throw cardinality_violation( error_message, sql_state );\n\n                  case '2':\n                     if( sql_state == \"2202E\" ) {\n                        throw array_subscript_error( error_message, sql_state );\n                     }\n                     if( sql_state == \"22021\" ) {\n                        throw character_not_in_repertoire( error_message, sql_state );\n                     }\n                     if( sql_state == \"22008\" ) {\n                        throw datetime_field_overflow( error_message, sql_state );\n                     }\n                     if( sql_state == \"22012\" ) {\n                        throw division_by_zero( error_message, sql_state );\n                     }\n                     if( sql_state == \"22005\" ) {\n                        throw error_in_assignment( error_message, sql_state );\n                     }\n                     if( sql_state == \"2200B\" ) {\n                        throw escape_character_conflict( error_message, sql_state );\n                     }\n                     if( sql_state == \"22022\" ) {\n                        throw indicator_overflow( error_message, sql_state );\n                     }\n                     if( sql_state == \"22015\" ) {\n                        throw interval_field_overflow( error_message, sql_state );\n                     }\n                     if( sql_state == \"2201E\" ) {\n                        throw invalid_argument_for_logarithm( error_message, sql_state );\n                     }\n                     if( sql_state == \"22014\" ) {\n                        throw invalid_argument_for_ntile_function( error_message, sql_state );\n                     }\n                     if( sql_state == \"22016\" ) {\n                        throw invalid_argument_for_nth_value_function( error_message, sql_state );\n                     }\n                     if( sql_state == \"2201F\" ) {\n                        throw invalid_argument_for_power_function( error_message, sql_state );\n                     }\n                     if( sql_state == \"2201G\" ) {\n                        throw invalid_argument_for_width_bucket_function( error_message, sql_state );\n                     }\n                     if( sql_state == \"22018\" ) {\n                        throw invalid_character_value_for_cast( error_message, sql_state );\n                     }\n                     if( sql_state == \"22007\" ) {\n                        throw invalid_datetime_format( error_message, sql_state );\n                     }\n                     if( sql_state == \"22019\" ) {\n                        throw invalid_escape_character( error_message, sql_state );\n                     }\n                     if( sql_state == \"2200D\" ) {\n                        throw invalid_escape_octet( error_message, sql_state );\n                     }\n                     if( sql_state == \"22025\" ) {\n                        throw invalid_escape_sequence( error_message, sql_state );\n                     }\n                     if( sql_state == \"22P06\" ) {\n                        throw nonstandard_use_of_escape_character( error_message, sql_state );\n                     }\n                     if( sql_state == \"22010\" ) {\n                        throw invalid_indicator_parameter_value( error_message, sql_state );\n                     }\n                     if( sql_state == \"22023\" ) {\n                        throw invalid_parameter_value( error_message, sql_state );\n                     }\n                     if( sql_state == \"22013\" ) {\n                        throw invalid_preceding_or_following_size( error_message, sql_state );\n                     }\n                     if( sql_state == \"2201B\" ) {\n                        throw invalid_regular_expression( error_message, sql_state );\n                     }\n                     if( sql_state == \"2201W\" ) {\n                        throw invalid_row_count_in_limit_clause( error_message, sql_state );\n                     }\n                     if( sql_state == \"2201X\" ) {\n                        throw invalid_row_count_in_result_offset_clause( error_message, sql_state );\n                     }\n                     if( sql_state == \"2202H\" ) {\n                        throw invalid_tablesample_argument( error_message, sql_state );\n                     }\n                     if( sql_state == \"2202G\" ) {\n                        throw invalid_tablesample_repeat( error_message, sql_state );\n                     }\n                     if( sql_state == \"22009\" ) {\n                        throw invalid_time_zone_displacement_value( error_message, sql_state );\n                     }\n                     if( sql_state == \"2200C\" ) {\n                        throw invalid_use_of_escape_character( error_message, sql_state );\n                     }\n                     if( sql_state == \"2200G\" ) {\n                        throw most_specific_type_mismatch( error_message, sql_state );\n                     }\n                     if( sql_state == \"22004\" ) {\n                        throw null_value_not_allowed( error_message, sql_state );\n                     }\n                     if( sql_state == \"22002\" ) {\n                        throw null_value_no_indicator_parameter( error_message, sql_state );\n                     }\n                     if( sql_state == \"22003\" ) {\n                        throw numeric_value_out_of_range( error_message, sql_state );\n                     }\n                     if( sql_state == \"2200H\" ) {\n                        throw sequence_generator_limit_exceeded( error_message, sql_state );\n                     }\n                     if( sql_state == \"22026\" ) {\n                        throw string_data_length_mismatch( error_message, sql_state );\n                     }\n                     if( sql_state == \"22001\" ) {\n                        throw string_data_right_truncation< data_exception >( error_message, sql_state );\n                     }\n                     if( sql_state == \"22011\" ) {\n                        throw substring_error( error_message, sql_state );\n                     }\n                     if( sql_state == \"22027\" ) {\n                        throw trim_error( error_message, sql_state );\n                     }\n                     if( sql_state == \"22024\" ) {\n                        throw unterminated_c_string( error_message, sql_state );\n                     }\n                     if( sql_state == \"2200F\" ) {\n                        throw zero_length_character_string( error_message, sql_state );\n                     }\n                     if( sql_state == \"22P01\" ) {\n                        throw floating_point_exception( error_message, sql_state );\n                     }\n                     if( sql_state == \"22P02\" ) {\n                        throw invalid_text_representation( error_message, sql_state );\n                     }\n                     if( sql_state == \"22P03\" ) {\n                        throw invalid_binary_representation( error_message, sql_state );\n                     }\n                     if( sql_state == \"22P04\" ) {\n                        throw bad_copy_file_format( error_message, sql_state );\n                     }\n                     if( sql_state == \"22P05\" ) {\n                        throw untranslatable_character( error_message, sql_state );\n                     }\n                     if( sql_state == \"2200L\" ) {\n                        throw not_an_xml_document( error_message, sql_state );\n                     }\n                     if( sql_state == \"2200M\" ) {\n                        throw invalid_xml_document( error_message, sql_state );\n                     }\n                     if( sql_state == \"2200N\" ) {\n                        throw invalid_xml_content( error_message, sql_state );\n                     }\n                     if( sql_state == \"2200S\" ) {\n                        throw invalid_xml_comment( error_message, sql_state );\n                     }\n                     if( sql_state == \"2200T\" ) {\n                        throw invalid_xml_processing_instruction( error_message, sql_state );\n                     }\n                     if( sql_state == \"22030\" ) {\n                        throw duplicate_json_object_key_value( error_message, sql_state );\n                     }\n                     if( sql_state == \"22031\" ) {\n                        throw invalid_argument_for_sql_json_datetime_function( error_message, sql_state );\n                     }\n                     if( sql_state == \"22032\" ) {\n                        throw invalid_json_text( error_message, sql_state );\n                     }\n                     if( sql_state == \"22033\" ) {\n                        throw invalid_sql_json_subscript( error_message, sql_state );\n                     }\n                     if( sql_state == \"22034\" ) {\n                        throw more_than_one_sql_json_item( error_message, sql_state );\n                     }\n                     if( sql_state == \"22035\" ) {\n                        throw no_sql_json_item( error_message, sql_state );\n                     }\n                     if( sql_state == \"22036\" ) {\n                        throw non_numeric_sql_json_item( error_message, sql_state );\n                     }\n                     if( sql_state == \"22037\" ) {\n                        throw non_unique_keys_in_a_json_object( error_message, sql_state );\n                     }\n                     if( sql_state == \"22038\" ) {\n                        throw singleton_sql_json_item_required( error_message, sql_state );\n                     }\n                     if( sql_state == \"22039\" ) {\n                        throw sql_json_array_not_found( error_message, sql_state );\n                     }\n                     if( sql_state == \"2203A\" ) {\n                        throw sql_json_member_not_found( error_message, sql_state );\n                     }\n                     if( sql_state == \"2203B\" ) {\n                        throw sql_json_number_not_found( error_message, sql_state );\n                     }\n                     if( sql_state == \"2203C\" ) {\n                        throw sql_json_object_not_found( error_message, sql_state );\n                     }\n                     if( sql_state == \"2203D\" ) {\n                        throw too_many_json_array_elements( error_message, sql_state );\n                     }\n                     if( sql_state == \"2203E\" ) {\n                        throw too_many_json_object_members( error_message, sql_state );\n                     }\n                     if( sql_state == \"2203F\" ) {\n                        throw sql_json_scalar_required( error_message, sql_state );\n                     }\n                     throw data_exception( error_message, sql_state );\n\n                  case '3':\n                     if( sql_state == \"23001\" ) {\n                        throw restrict_violation( error_message, sql_state );\n                     }\n                     if( sql_state == \"23502\" ) {\n                        throw not_null_violation( error_message, sql_state );\n                     }\n                     if( sql_state == \"23503\" ) {\n                        throw foreign_key_violation( error_message, sql_state );\n                     }\n                     if( sql_state == \"23505\" ) {\n                        throw unique_violation( error_message, sql_state );\n                     }\n                     if( sql_state == \"23514\" ) {\n                        throw check_violation( error_message, sql_state );\n                     }\n                     if( sql_state == \"23P01\" ) {\n                        throw exclusion_violation( error_message, sql_state );\n                     }\n                     throw integrity_constraint_violation( error_message, sql_state );\n\n                  case '4':\n                     throw invalid_cursor_state( error_message, sql_state );\n\n                  case '5':\n                     if( sql_state == \"25001\" ) {\n                        throw active_sql_transaction( error_message, sql_state );\n                     }\n                     if( sql_state == \"25002\" ) {\n                        throw branch_transaction_already_active( error_message, sql_state );\n                     }\n                     if( sql_state == \"25008\" ) {\n                        throw held_cursor_requires_same_isolation_level( error_message, sql_state );\n                     }\n                     if( sql_state == \"25003\" ) {\n                        throw inappropriate_access_mode_for_branch_transaction( error_message, sql_state );\n                     }\n                     if( sql_state == \"25004\" ) {\n                        throw inappropriate_isolation_level_for_branch_transaction( error_message, sql_state );\n                     }\n                     if( sql_state == \"25005\" ) {\n                        throw no_active_sql_transaction_for_branch_transaction( error_message, sql_state );\n                     }\n                     if( sql_state == \"25006\" ) {\n                        throw read_only_sql_transaction( error_message, sql_state );\n                     }\n                     if( sql_state == \"25007\" ) {\n                        throw schema_and_data_statement_mixing_not_supported( error_message, sql_state );\n                     }\n                     if( sql_state == \"25P01\" ) {\n                        throw no_active_sql_transaction( error_message, sql_state );\n                     }\n                     if( sql_state == \"25P02\" ) {\n                        throw in_failed_sql_transaction( error_message, sql_state );\n                     }\n                     if( sql_state == \"25P03\" ) {\n                        throw idle_in_transaction_session_timeout( error_message, sql_state );\n                     }\n                     throw invalid_transaction_state( error_message, sql_state );\n\n                  case '6':\n                     throw invalid_sql_statement_name( error_message, sql_state );\n\n                  case '7':\n                     throw triggered_data_change_violation( error_message, sql_state );\n\n                  case '8':\n                     if( sql_state == \"28P01\" ) {\n                        throw invalid_password( error_message, sql_state );\n                     }\n                     throw invalid_authorization_specification( error_message, sql_state );\n\n                  case 'B':\n                     if( sql_state == \"2BP01\" ) {\n                        throw dependent_objects_still_exist( error_message, sql_state );\n                     }\n                     throw dependent_privilege_descriptors_still_exist( error_message, sql_state );\n\n                  case 'D':\n                     throw invalid_transaction_termination( error_message, sql_state );\n\n                  case 'F':\n                     if( sql_state == \"2F002\" ) {\n                        throw modifying_sql_data_not_permitted< sql_routine_exception >( error_message, sql_state );\n                     }\n                     if( sql_state == \"2F003\" ) {\n                        throw prohibited_sql_statement_attempted< sql_routine_exception >( error_message, sql_state );\n                     }\n                     if( sql_state == \"2F004\" ) {\n                        throw reading_sql_data_not_permitted< sql_routine_exception >( error_message, sql_state );\n                     }\n                     if( sql_state == \"2F005\" ) {\n                        throw function_executed_no_return_statement( error_message, sql_state );\n                     }\n                     throw sql_routine_exception( error_message, sql_state );\n\n                  default:\n                     throw sql_error( error_message, sql_state );\n               }\n\n            case '3':\n               switch( sql_state[ 1 ] ) {\n                  case '4':\n                     throw invalid_cursor_name( error_message, sql_state );\n\n                  case '8':\n                     if( sql_state == \"38001\" ) {\n                        throw containing_sql_not_permitted( error_message, sql_state );\n                     }\n                     if( sql_state == \"38002\" ) {\n                        throw modifying_sql_data_not_permitted< external_routine_exception >( error_message, sql_state );\n                     }\n                     if( sql_state == \"38003\" ) {\n                        throw prohibited_sql_statement_attempted< external_routine_exception >( error_message, sql_state );\n                     }\n                     if( sql_state == \"38004\" ) {\n                        throw reading_sql_data_not_permitted< external_routine_exception >( error_message, sql_state );\n                     }\n                     throw external_routine_exception( error_message, sql_state );\n\n                  case '9':\n                     if( sql_state == \"39001\" ) {\n                        throw invalid_sqlstate_returned( error_message, sql_state );\n                     }\n                     if( sql_state == \"39004\" ) {\n                        throw external_null_value_not_allowed( error_message, sql_state );\n                     }\n                     if( sql_state == \"39P01\" ) {\n                        throw trigger_protocol_violated( error_message, sql_state );\n                     }\n                     if( sql_state == \"39P02\" ) {\n                        throw srf_protocol_violated( error_message, sql_state );\n                     }\n                     if( sql_state == \"39P03\" ) {\n                        throw event_trigger_protocol_violated( error_message, sql_state );\n                     }\n                     throw external_routine_invocation_exception( error_message, sql_state );\n\n                  case 'B':\n                     if( sql_state == \"3B001\" ) {\n                        throw invalid_savepoint_specification( error_message, sql_state );\n                     }\n                     throw savepoint_exception( error_message, sql_state );\n\n                  case 'D':\n                     throw invalid_catalog_name( error_message, sql_state );\n\n                  case 'F':\n                     throw invalid_schema_name( error_message, sql_state );\n\n                  default:\n                     throw sql_error( error_message, sql_state );\n               }\n\n            case '4':\n               switch( sql_state[ 1 ] ) {\n                  case '0':\n                     if( sql_state == \"40001\" ) {\n                        throw serialization_failure( error_message, sql_state );\n                     }\n                     if( sql_state == \"40002\" ) {\n                        throw transaction_integrity_constraint_violation( error_message, sql_state );\n                     }\n                     if( sql_state == \"40003\" ) {\n                        throw statement_completion_unknown( error_message, sql_state );\n                     }\n                     if( sql_state == \"40P01\" ) {\n                        throw deadlock_detected( error_message, sql_state );\n                     }\n                     throw transaction_rollback( error_message, sql_state );\n\n                  case '2':\n                     if( sql_state == \"42501\" ) {\n                        throw insufficient_privilege( error_message, sql_state );\n                     }\n                     if( sql_state == \"42601\" ) {\n                        throw syntax_error( error_message, sql_state );\n                     }\n                     if( sql_state == \"42602\" ) {\n                        throw invalid_name( error_message, sql_state );\n                     }\n                     if( sql_state == \"42611\" ) {\n                        throw invalid_column_definition( error_message, sql_state );\n                     }\n                     if( sql_state == \"42622\" ) {\n                        throw name_too_long( error_message, sql_state );\n                     }\n                     if( sql_state == \"42701\" ) {\n                        throw duplicate_column( error_message, sql_state );\n                     }\n                     if( sql_state == \"42702\" ) {\n                        throw ambiguous_column( error_message, sql_state );\n                     }\n                     if( sql_state == \"42703\" ) {\n                        throw undefined_column( error_message, sql_state );\n                     }\n                     if( sql_state == \"42704\" ) {\n                        throw undefined_object( error_message, sql_state );\n                     }\n                     if( sql_state == \"42710\" ) {\n                        throw duplicate_object( error_message, sql_state );\n                     }\n                     if( sql_state == \"42712\" ) {\n                        throw duplicate_alias( error_message, sql_state );\n                     }\n                     if( sql_state == \"42723\" ) {\n                        throw duplicate_function( error_message, sql_state );\n                     }\n                     if( sql_state == \"42725\" ) {\n                        throw ambiguous_function( error_message, sql_state );\n                     }\n                     if( sql_state == \"42803\" ) {\n                        throw grouping_error( error_message, sql_state );\n                     }\n                     if( sql_state == \"42804\" ) {\n                        throw datatype_mismatch( error_message, sql_state );\n                     }\n                     if( sql_state == \"42809\" ) {\n                        throw wrong_object_type( error_message, sql_state );\n                     }\n                     if( sql_state == \"42830\" ) {\n                        throw invalid_foreign_key( error_message, sql_state );\n                     }\n                     if( sql_state == \"42846\" ) {\n                        throw cannot_coerce( error_message, sql_state );\n                     }\n                     if( sql_state == \"42883\" ) {\n                        throw undefined_function( error_message, sql_state );\n                     }\n                     if( sql_state == \"428C9\" ) {\n                        throw generated_always( error_message, sql_state );\n                     }\n                     if( sql_state == \"42939\" ) {\n                        throw reserved_name( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P01\" ) {\n                        throw undefined_table( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P02\" ) {\n                        throw undefined_parameter( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P03\" ) {\n                        throw duplicate_cursor( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P04\" ) {\n                        throw duplicate_database( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P05\" ) {\n                        throw duplicate_prepared_statement( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P06\" ) {\n                        throw duplicate_schema( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P07\" ) {\n                        throw duplicate_table( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P08\" ) {\n                        throw ambiguous_parameter( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P09\" ) {\n                        throw ambiguous_alias( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P10\" ) {\n                        throw invalid_column_reference( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P11\" ) {\n                        throw invalid_cursor_definition( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P12\" ) {\n                        throw invalid_database_definition( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P13\" ) {\n                        throw invalid_function_definition( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P14\" ) {\n                        throw invalid_prepared_statement_definition( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P15\" ) {\n                        throw invalid_schema_definition( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P16\" ) {\n                        throw invalid_table_definition( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P17\" ) {\n                        throw invalid_object_definition( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P18\" ) {\n                        throw indeterminate_datatype( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P19\" ) {\n                        throw invalid_recursion( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P20\" ) {\n                        throw windowing_error( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P21\" ) {\n                        throw collation_mismatch( error_message, sql_state );\n                     }\n                     if( sql_state == \"42P22\" ) {\n                        throw indeterminate_collation( error_message, sql_state );\n                     }\n                     throw syntax_error_or_access_rule_violation( error_message, sql_state );\n\n                  case '4':\n                     throw with_check_option_violation( error_message, sql_state );\n\n                  default:\n                     throw sql_error( error_message, sql_state );\n               }\n\n            case '5':\n               switch( sql_state[ 1 ] ) {\n                  case '3':\n                     if( sql_state == \"53100\" ) {\n                        throw disk_full( error_message, sql_state );\n                     }\n                     if( sql_state == \"53200\" ) {\n                        throw out_of_memory( error_message, sql_state );\n                     }\n                     if( sql_state == \"53300\" ) {\n                        throw too_many_connections( error_message, sql_state );\n                     }\n                     if( sql_state == \"53400\" ) {\n                        throw configuration_limit_exceeded( error_message, sql_state );\n                     }\n                     throw insufficient_resources( error_message, sql_state );\n\n                  case '4':\n                     if( sql_state == \"54001\" ) {\n                        throw statement_too_complex( error_message, sql_state );\n                     }\n                     if( sql_state == \"54011\" ) {\n                        throw too_many_columns( error_message, sql_state );\n                     }\n                     if( sql_state == \"54023\" ) {\n                        throw too_many_arguments( error_message, sql_state );\n                     }\n                     throw program_limit_exceeded( error_message, sql_state );\n\n                  case '5':\n                     if( sql_state == \"55006\" ) {\n                        throw object_in_use( error_message, sql_state );\n                     }\n                     if( sql_state == \"55P02\" ) {\n                        throw cant_change_runtime_param( error_message, sql_state );\n                     }\n                     if( sql_state == \"55P03\" ) {\n                        throw lock_not_available( error_message, sql_state );\n                     }\n                     if( sql_state == \"55P04\" ) {\n                        throw unsafe_new_enum_value_usage( error_message, sql_state );\n                     }\n                     throw object_not_in_prerequisite_state( error_message, sql_state );\n\n                  case '7':\n                     if( sql_state == \"57014\" ) {\n                        throw query_canceled( error_message, sql_state );\n                     }\n                     if( sql_state == \"57P01\" ) {\n                        throw admin_shutdown( error_message, sql_state );\n                     }\n                     if( sql_state == \"57P02\" ) {\n                        throw crash_shutdown( error_message, sql_state );\n                     }\n                     if( sql_state == \"57P03\" ) {\n                        throw cannot_connect_now( error_message, sql_state );\n                     }\n                     if( sql_state == \"57P04\" ) {\n                        throw database_dropped( error_message, sql_state );\n                     }\n                     throw operator_intervention( error_message, sql_state );\n\n                  case '8':\n                     if( sql_state == \"58030\" ) {\n                        throw io_error( error_message, sql_state );\n                     }\n                     if( sql_state == \"58P01\" ) {\n                        throw undefined_file( error_message, sql_state );\n                     }\n                     if( sql_state == \"58P02\" ) {\n                        throw duplicate_file( error_message, sql_state );\n                     }\n                     throw system_error( error_message, sql_state );\n\n                  default:\n                     throw sql_error( error_message, sql_state );\n               }\n\n            case '7':\n               switch( sql_state[ 1 ] ) {\n                  case '2':\n                     throw snapshot_too_old( error_message, sql_state );\n\n                  default:\n                     throw sql_error( error_message, sql_state );\n               }\n\n            case 'F':\n               switch( sql_state[ 1 ] ) {\n                  case '0':\n                     if( sql_state == \"F0001\" ) {\n                        throw lock_file_exists( error_message, sql_state );\n                     }\n                     throw config_file_error( error_message, sql_state );\n\n                  default:\n                     throw sql_error( error_message, sql_state );\n               }\n\n            case 'H':\n               switch( sql_state[ 1 ] ) {\n                  case 'V':\n                     if( sql_state == \"HV001\" ) {\n                        throw fdw_out_of_memory( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV002\" ) {\n                        throw fdw_dynamic_parameter_value_needed( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV004\" ) {\n                        throw fdw_invalid_data_type( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV005\" ) {\n                        throw fdw_column_name_not_found( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV006\" ) {\n                        throw fdw_invalid_data_type_descriptors( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV007\" ) {\n                        throw fdw_invalid_column_name( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV008\" ) {\n                        throw fdw_invalid_column_number( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV009\" ) {\n                        throw fdw_invalid_use_of_null_pointer( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00A\" ) {\n                        throw fdw_invalid_string_format( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00B\" ) {\n                        throw fdw_invalid_handle( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00C\" ) {\n                        throw fdw_invalid_option_index( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00D\" ) {\n                        throw fdw_invalid_option_name( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00J\" ) {\n                        throw fdw_option_name_not_found( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00K\" ) {\n                        throw fdw_reply_handle( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00L\" ) {\n                        throw fdw_unable_to_create_execution( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00M\" ) {\n                        throw fdw_unable_to_create_reply( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00N\" ) {\n                        throw fdw_unable_to_establish_connection( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00P\" ) {\n                        throw fdw_no_schemas( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00Q\" ) {\n                        throw fdw_schema_not_found( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV00R\" ) {\n                        throw fdw_table_not_found( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV010\" ) {\n                        throw fdw_function_sequence_error( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV014\" ) {\n                        throw fdw_too_many_handles( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV021\" ) {\n                        throw fdw_inconsistent_descriptor_information( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV024\" ) {\n                        throw fdw_invalid_attribute_value( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV090\" ) {\n                        throw fdw_invalid_string_length_or_buffer_length( error_message, sql_state );\n                     }\n                     if( sql_state == \"HV091\" ) {\n                        throw fdw_invalid_descriptor_field_identifier( error_message, sql_state );\n                     }\n                     throw fdw_error( error_message, sql_state );\n\n                  default:\n                     throw sql_error( error_message, sql_state );\n               }\n\n            case 'P':\n               switch( sql_state[ 1 ] ) {\n                  case '0':\n                     if( sql_state == \"P0001\" ) {\n                        throw raise_exception( error_message, sql_state );\n                     }\n                     if( sql_state == \"P0002\" ) {\n                        throw no_data_found( error_message, sql_state );\n                     }\n                     if( sql_state == \"P0003\" ) {\n                        throw too_many_rows( error_message, sql_state );\n                     }\n                     if( sql_state == \"P0004\" ) {\n                        throw assert_failure( error_message, sql_state );\n                     }\n                     throw plpgsql_error( error_message, sql_state );\n\n                  default:\n                     throw sql_error( error_message, sql_state );\n               }\n\n            case 'X':\n               switch( sql_state[ 1 ] ) {\n                  case 'X':\n                     if( sql_state == \"XX001\" ) {\n                        throw data_corrupted( error_message, sql_state );\n                     }\n                     if( sql_state == \"XX002\" ) {\n                        throw index_corrupted( error_message, sql_state );\n                     }\n                     throw internal_error( error_message, sql_state );\n\n                  default:\n                     throw sql_error( error_message, sql_state );\n               }\n\n            default:\n               throw sql_error( error_message, sql_state );\n         }\n         // LCOV_EXCL_STOP\n      }\n\n   }  // namespace internal\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/field.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/field.hpp>\n\n#include <cstddef>\n#include <string>\n\n#include <tao/pq/row.hpp>\n\nnamespace tao::pq\n{\n   auto field::name() const -> std::string\n   {\n      return m_row->name( m_column );\n   }\n\n   auto field::index() const noexcept -> std::size_t\n   {\n      return m_column - m_row->m_offset;\n   }\n\n   auto field::is_null() const -> bool\n   {\n      return m_row->is_null( m_column );\n   }\n\n   auto field::get() const -> const char*\n   {\n      return m_row->get( m_column );\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/internal/demangle.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/internal/demangle.hpp>\n\n#include <string>\n\n#if !defined( _WIN32 )\n#include <cstdlib>\n#include <cxxabi.h>\n#include <memory>\n#endif\n\nnamespace tao::pq::internal\n{\n   auto demangle( const char* const symbol ) -> std::string\n   {\n#if defined( _WIN32 )\n      return symbol;\n#else\n      const std::unique_ptr< char, decltype( &std::free ) > demangled( abi::__cxa_demangle( symbol, nullptr, nullptr, nullptr ), &std::free );\n      return demangled ? demangled.get() : symbol;\n#endif\n   }\n\n}  // namespace tao::pq::internal\n"
  },
  {
    "path": "src/lib/pq/internal/poll.cpp",
    "content": "// Copyright (c) 2023-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/internal/poll.hpp>\n\n#include <cctype>\n#include <cerrno>\n#include <cstring>\n#include <format>\n#include <string>\n\n#if defined( _WIN32 )\n#include <winsock2.h>\n#else\n#include <poll.h>\n#endif\n\n#include <tao/pq/exception.hpp>\n#include <tao/pq/internal/unreachable.hpp>\n#include <tao/pq/poll.hpp>\n\nnamespace tao::pq::internal\n{\n   namespace\n   {\n      // LCOV_EXCL_START\n      [[nodiscard, maybe_unused]] auto errno_result_to_string( const int e, char* buffer, int result ) -> std::string\n      {\n         if( result == 0 ) {\n            return buffer;\n         }\n         return std::format( \"unknown error code {}\", e );\n      }\n\n      [[nodiscard, maybe_unused]] auto errno_result_to_string( const int /*unused*/, char* /*unused*/, char* result ) -> std::string\n      {\n         return result;\n      }\n\n      [[nodiscard]] auto errno_to_string( const int e ) -> std::string\n      {\n         char buffer[ 256 ];\n#if defined( _WIN32 )\n#ifdef _MSC_VER\n         return errno_result_to_string( e, buffer, strerror_s( buffer, e ) );\n#else\n         return errno_result_to_string( e, buffer, strerror_s( buffer, sizeof( buffer ), e ) );\n#endif\n#else\n         return errno_result_to_string( e, buffer, strerror_r( e, buffer, sizeof( buffer ) ) );\n#endif\n      }\n      // LCOV_EXCL_STOP\n\n   }  // namespace\n\n   auto poll( const int socket, const bool wait_for_write, const int timeout_ms ) -> pq::poll::status\n   {\n#if defined( _WIN32 )\n\n      const short events = POLLIN | ( wait_for_write ? POLLOUT : 0 );\n      WSAPOLLFD pfd = { static_cast< SOCKET >( socket ), events, 0 };\n      const auto result = WSAPoll( &pfd, 1, timeout_ms );\n      switch( result ) {\n         case 0:\n            return pq::poll::status::timeout;\n\n         case 1:\n            if( ( pfd.revents & events ) == 0 ) {\n               throw network_error( std::format( \"WSAPoll() failed, events {}, revents {}\", events, pfd.revents ) );\n            }\n            return ( ( pfd.revents & POLLIN ) != 0 ) ? pq::poll::status::readable : pq::poll::status::writable;\n\n         case SOCKET_ERROR: {\n            const int e = WSAGetLastError();\n            throw network_error( std::format( \"WSAPoll() failed: {}\", errno_to_string( e ) ) );\n         }\n\n         default:\n            TAO_PQ_INTERNAL_UNREACHABLE;\n      }\n\n#else\n\n      const short events = POLLIN | ( wait_for_write ? POLLOUT : 0 );\n      pollfd pfd = {\n         .fd = socket,\n         .events = events,\n         .revents = 0\n      };\n      errno = 0;\n      const auto result = ::poll( &pfd, 1, timeout_ms );\n      switch( result ) {\n         case 0:\n            return pq::poll::status::timeout;\n\n         case 1:\n            if( ( pfd.revents & events ) == 0 ) {\n               throw network_error( std::format( \"poll() failed, events {}, revents {}\", events, pfd.revents ) );  // LCOV_EXCL_LINE\n            }\n            return ( ( pfd.revents & POLLIN ) != 0 ) ? pq::poll::status::readable : pq::poll::status::writable;\n\n            // LCOV_EXCL_START\n         case -1: {\n            const int e = errno;\n            if( ( e != EINTR ) && ( e != EAGAIN ) ) {\n               throw network_error( std::format( \"poll() failed: \", errno_to_string( e ) ) );\n            }\n            return pq::poll::status::again;\n         }\n\n         default:\n            TAO_PQ_INTERNAL_UNREACHABLE;\n      }\n         // LCOV_EXCL_STOP\n#endif\n   }\n\n}  // namespace tao::pq::internal\n"
  },
  {
    "path": "src/lib/pq/internal/strtox.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <cassert>\n#include <cctype>\n#include <cerrno>\n#include <cstdlib>\n#include <format>\n#include <stdexcept>\n#include <string>\n\n#include <tao/pq/internal/strtox.hpp>\n\nnamespace tao::pq::internal\n{\n   namespace\n   {\n      template< typename >\n      inline constexpr const char* message = nullptr;\n\n      // clang-format off\n      template<> inline constexpr auto message< float > = \"tao::pq::internal::strtof() failed for input: \";\n      template<> inline constexpr auto message< double > = \"tao::pq::internal::strtod() failed for input: \";\n      template<> inline constexpr auto message< long double > = \"tao::pq::internal::strtold() failed for input: \";\n      // clang-format on\n\n      template< typename T >\n      [[nodiscard]] auto failure_message( const char* input ) -> std::string\n      {\n         return std::string( message< T > ) + input;\n      }\n\n      template< typename T >\n      [[nodiscard]] auto call_floating_point( const char* nptr, char** endptr ) -> T;\n\n      template<>\n      [[nodiscard]] auto call_floating_point< float >( const char* nptr, char** endptr ) -> float\n      {\n         return std::strtof( nptr, endptr );\n      }\n\n      template<>\n      [[nodiscard]] auto call_floating_point< double >( const char* nptr, char** endptr ) -> double\n      {\n         return std::strtod( nptr, endptr );\n      }\n\n      template<>\n      [[nodiscard]] auto call_floating_point< long double >( const char* nptr, char** endptr ) -> long double\n      {\n         return std::strtold( nptr, endptr );\n      }\n\n      template< typename T >\n      [[nodiscard]] auto str_to_floating_point( const char* input ) -> T\n      {\n         assert( input );\n         if( *input == '\\0' || std::isspace( *input ) ) {\n            throw std::runtime_error( failure_message< T >( input ) );\n         }\n         char* end;\n         errno = 0;\n         const T result = call_floating_point< T >( input, &end );\n         switch( errno ) {\n            case 0:\n               if( *end == '\\0' ) {\n                  return result;\n               }\n               throw std::runtime_error( failure_message< T >( input ) );\n\n            case ERANGE:\n               if( result == 0 ) {\n                  throw std::underflow_error( failure_message< T >( input ) );\n               }\n               else {\n                  throw std::overflow_error( failure_message< T >( input ) );\n               }\n\n            default:\n               throw std::runtime_error( std::format( \"code should be unreachable, errno: {}, input: \\\"{}\\\"\", errno, input ) );  // LCOV_EXCL_LINE\n         }\n      }\n\n   }  // namespace\n\n   [[nodiscard]] auto strtof( const char* input ) -> float\n   {\n      return str_to_floating_point< float >( input );\n   }\n\n   [[nodiscard]] auto strtod( const char* input ) -> double\n   {\n      return str_to_floating_point< double >( input );\n   }\n\n   [[nodiscard]] auto strtold( const char* input ) -> long double\n   {\n      return str_to_floating_point< long double >( input );\n   }\n\n}  // namespace tao::pq::internal\n"
  },
  {
    "path": "src/lib/pq/large_object.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/large_object.hpp>\n\n#include <cassert>\n#include <cstddef>\n#include <cstdint>\n#include <cstdio>\n#include <format>\n#include <ios>\n#include <memory>\n#include <stdexcept>\n#include <string>\n#include <utility>\n\n#include <libpq-fe.h>\n#include <libpq/libpq-fs.h>\n\n#include <tao/pq/binary.hpp>\n#include <tao/pq/connection.hpp>\n#include <tao/pq/internal/resize_uninitialized.hpp>\n#include <tao/pq/oid.hpp>\n#include <tao/pq/transaction.hpp>\n\nnamespace tao::pq\n{\n   namespace\n   {\n      [[nodiscard]] constexpr auto to_mode( const std::ios_base::openmode m ) noexcept -> int\n      {\n         return ( ( ( m & std::ios_base::in ) != 0 ) ? INV_READ : 0 ) | ( ( ( m & std::ios_base::out ) != 0 ) ? INV_WRITE : 0 );\n      }\n\n   }  // namespace\n\n   auto large_object::create( const std::shared_ptr< transaction >& transaction, const oid desired_id ) -> oid\n   {\n      const oid id = static_cast< oid >( lo_create( transaction->connection()->underlying_raw_ptr(), static_cast< Oid >( desired_id ) ) );\n      if( id == oid::invalid ) {\n         throw std::runtime_error( std::format( \"tao::pq::large_object::create() failed: {}\", transaction->connection()->error_message() ) );\n      }\n      return id;\n   }\n\n   void large_object::remove( const std::shared_ptr< transaction >& transaction, const oid id )\n   {\n      if( lo_unlink( transaction->connection()->underlying_raw_ptr(), static_cast< Oid >( id ) ) == -1 ) {\n         throw std::runtime_error( std::format( \"tao::pq::large_object::remove() failed: {}\", transaction->connection()->error_message() ) );\n      }\n   }\n\n   auto large_object::import_file( const std::shared_ptr< transaction >& transaction, const char* filename, const oid desired_id ) -> oid\n   {\n      const oid id = static_cast< oid >( lo_import_with_oid( transaction->connection()->underlying_raw_ptr(), filename, static_cast< Oid >( desired_id ) ) );\n      if( id == oid::invalid ) {\n         throw std::runtime_error( std::format( \"tao::pq::large_object::import_file() failed: {}\", transaction->connection()->error_message() ) );\n      }\n      return id;\n   }\n\n   void large_object::export_file( const std::shared_ptr< transaction >& transaction, const oid id, const char* filename )\n   {\n      if( lo_export( transaction->connection()->underlying_raw_ptr(), static_cast< Oid >( id ), filename ) == -1 ) {\n         throw std::runtime_error( std::format( \"tao::pq::large_object::export_file() failed: {}\", transaction->connection()->error_message() ) );\n      }\n   }\n\n   large_object::large_object( const std::shared_ptr< transaction >& transaction, const oid id, const std::ios_base::openmode m )\n      : m_transaction( transaction ),\n        m_fd( lo_open( transaction->connection()->underlying_raw_ptr(), static_cast< Oid >( id ), to_mode( m ) ) )\n   {\n      if( m_fd == -1 ) {\n         throw std::runtime_error( std::format( \"tao::pq::large_object::open() failed: {}\", transaction->connection()->error_message() ) );\n      }\n   }\n\n   large_object::large_object( large_object&& other ) noexcept\n      : m_transaction( std::move( other.m_transaction ) ),\n        m_fd( other.m_fd )\n   {}\n\n   large_object::~large_object()\n   {\n      try {\n         close();\n      }\n      // LCOV_EXCL_START\n      catch( ... ) {  // NOLINT(bugprone-empty-catch)\n         // TODO: How to handle this case properly?\n      }\n      // LCOV_EXCL_STOP\n   }\n\n   auto large_object::operator=( large_object&& rhs ) -> large_object&  // NOLINT\n   {\n      close();\n      m_transaction = std::move( rhs.m_transaction );\n      m_fd = rhs.m_fd;\n      return *this;\n   }\n\n   void large_object::close()\n   {\n      if( m_transaction ) {\n         if( lo_close( m_transaction->connection()->underlying_raw_ptr(), m_fd ) == -1 ) {\n            throw std::runtime_error( std::format( \"tao::pq::large_object::close() failed: {}\", m_transaction->connection()->error_message() ) );\n         }\n         m_transaction.reset();\n      }\n   }\n\n   auto large_object::read( char* data, const std::size_t size ) -> std::size_t\n   {\n      assert( m_transaction );\n      const auto result = lo_read( m_transaction->connection()->underlying_raw_ptr(), m_fd, data, size );\n      if( result == -1 ) {\n         throw std::runtime_error( std::format( \"tao::pq::large_object::read() failed: {}\", m_transaction->connection()->error_message() ) );\n      }\n      return result;\n   }\n\n   void large_object::write( const char* data, const std::size_t size )\n   {\n      assert( m_transaction );\n      if( lo_write( m_transaction->connection()->underlying_raw_ptr(), m_fd, data, size ) == -1 ) {\n         throw std::runtime_error( std::format( \"tao::pq::large_object::write() failed: {}\", m_transaction->connection()->error_message() ) );\n      }\n   }\n\n   void large_object::resize( const std::int64_t size )\n   {\n      assert( m_transaction );\n      if( lo_truncate64( m_transaction->connection()->underlying_raw_ptr(), m_fd, size ) == -1 ) {\n         throw std::runtime_error( std::format( \"tao::pq::large_object::resize() failed: {}\", m_transaction->connection()->error_message() ) );\n      }\n   }\n\n   auto large_object::seek( const std::int64_t offset, const std::ios_base::seekdir whence ) -> std::int64_t\n   {\n      static_assert( std::ios_base::beg == SEEK_SET );\n      static_assert( std::ios_base::cur == SEEK_CUR );\n      static_assert( std::ios_base::end == SEEK_END );\n\n      assert( m_transaction );\n      const auto result = lo_lseek64( m_transaction->connection()->underlying_raw_ptr(), m_fd, offset, whence );\n      if( result == -1 ) {\n         throw std::runtime_error( std::format( \"tao::pq::large_object::seek() failed: {}\", m_transaction->connection()->error_message() ) );\n      }\n      return result;\n   }\n\n   auto large_object::tell() const -> std::int64_t\n   {\n      assert( m_transaction );\n      const auto pos = lo_tell64( m_transaction->connection()->underlying_raw_ptr(), m_fd );\n      if( pos == -1 ) {\n         throw std::runtime_error( std::format( \"tao::pq::large_object::tell() failed: {}\", m_transaction->connection()->error_message() ) );\n      }\n      return pos;\n   }\n\n   template<>\n   auto large_object::read< std::string >( const std::size_t size ) -> std::string\n   {\n      std::string nrv;\n      internal::resize_uninitialized( nrv, size );\n      nrv.resize( read( nrv.data(), size ) );\n      return nrv;\n   }\n\n   template<>\n   auto large_object::read< binary >( const std::size_t size ) -> binary\n   {\n      binary nrv;\n      internal::resize_uninitialized( nrv, size );\n      nrv.resize( read( nrv.data(), size ) );\n      return nrv;\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/parameter_traits.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/parameter_traits.hpp>\n\n#include <string>\n#include <string_view>\n\nnamespace tao::pq::internal\n{\n   void array_append( std::string& buffer, std::string_view data )\n   {\n      if( data.empty() ) {\n         buffer += \"\\\"\\\"\";\n      }\n      else if( data == \"NULL\" ) {\n         buffer += \"\\\"NULL\\\"\";\n      }\n      else if( data.find_first_of( \"\\\\\\\"{},; \\t\" ) != std::string_view::npos ) {\n         buffer += '\"';\n         while( true ) {\n            const auto n = data.find_first_of( \"\\\\\\\"\" );\n            if( n == std::string_view::npos ) {\n               buffer += data;\n               break;\n            }\n            buffer.append( data.data(), n );  // NOLINT(bugprone-suspicious-stringview-data-usage)\n            buffer += '\\\\';\n            buffer += data[ n ];\n            data.remove_prefix( n + 1 );\n         }\n         buffer += '\"';\n      }\n      else {\n         buffer += data;\n      }\n   }\n\n   void table_writer_append( std::string& buffer, std::string_view data )\n   {\n      while( true ) {\n         const auto n = data.find_first_of( \"\\b\\f\\n\\r\\t\\v\\\\\" );\n         if( n == std::string_view::npos ) {\n            buffer += data;\n            return;\n         }\n         buffer.append( data.data(), n );  // NOLINT(bugprone-suspicious-stringview-data-usage)\n         buffer += '\\\\';\n         buffer += data[ n ];\n         data.remove_prefix( n + 1 );\n      }\n   }\n\n}  // namespace tao::pq::internal\n"
  },
  {
    "path": "src/lib/pq/pipeline.cpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/pipeline.hpp>\n\n#include <chrono>\n\n#include <tao/pq/connection.hpp>\n\nnamespace tao::pq\n{\n   pipeline::pipeline( const pipeline::private_key /*unused*/, const std::shared_ptr< pq::connection >& connection )\n      : transaction_base( connection ),\n        m_previous( current_transaction()->shared_from_this() )\n   {\n      connection->enter_pipeline_mode();\n      current_transaction() = this;\n   }\n\n   void pipeline::sync()\n   {\n      connection()->pipeline_sync();\n   }\n\n   void pipeline::consume_sync( const std::chrono::steady_clock::time_point start )\n   {\n      current_transaction()->consume_pipeline_sync( start );\n   }\n\n   void pipeline::finish()\n   {\n      if( m_previous ) {\n         current_transaction() = m_previous.get();\n         const auto extend_lifetime = std::move( m_previous );\n         connection()->exit_pipeline_mode();\n      }\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/result.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/result.hpp>\n\n#include <cassert>\n#include <cstddef>\n#include <format>\n#include <stdexcept>\n#include <string>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/exception.hpp>\n#include <tao/pq/internal/from_chars.hpp>\n#include <tao/pq/internal/unreachable.hpp>\n#include <tao/pq/internal/zsv.hpp>\n\nnamespace tao::pq\n{\n   void result::check_row( const std::size_t row ) const\n   {\n      assert( m_columns != 0 );\n      if( !( row < m_rows ) ) {\n         if( m_rows == 0 ) {\n            throw std::out_of_range( std::format( \"row {} out of range, result is empty\", row ) );\n         }\n         throw std::out_of_range( std::format( \"row {} out of range (0-{})\", row, m_rows - 1 ) );\n      }\n   }\n\n   result::result( PGresult* pgresult )\n      : m_pgresult( pgresult, &PQclear ),\n        m_columns( PQnfields( pgresult ) ),\n        m_rows( PQntuples( pgresult ) )\n   {\n      switch( PQresultStatus( pgresult ) ) {\n         case PGRES_COMMAND_OK:\n         case PGRES_TUPLES_OK:\n         case PGRES_SINGLE_TUPLE:\n#if defined( LIBPQ_HAS_CHUNK_MODE )\n         case PGRES_TUPLES_CHUNK:\n#endif\n         case PGRES_PIPELINE_SYNC:\n         case PGRES_PIPELINE_ABORTED:\n            return;\n\n         case PGRES_EMPTY_QUERY:\n            throw std::runtime_error( \"unexpected empty query\" );\n\n         case PGRES_COPY_IN:\n         case PGRES_COPY_OUT:\n            TAO_PQ_INTERNAL_UNREACHABLE;  // LCOV_EXCL_LINE\n\n         default:\n            internal::throw_sqlstate( pgresult );\n      }\n   }\n\n   auto result::status() const noexcept -> result_status\n   {\n      return static_cast< pq::result_status >( PQresultStatus( m_pgresult.get() ) );\n   }\n\n   auto result::has_rows_affected() const noexcept -> bool\n   {\n      const char* str = PQcmdTuples( m_pgresult.get() );\n      return str[ 0 ] != '\\0';\n   }\n\n   auto result::rows_affected() const -> std::size_t\n   {\n      const char* str = PQcmdTuples( m_pgresult.get() );\n      if( str[ 0 ] == '\\0' ) {\n         throw std::logic_error( \"statement does not return affected rows\" );\n      }\n      return internal::from_chars< std::size_t >( str );\n   }\n\n   auto result::name( const std::size_t column ) const -> std::string\n   {\n      if( column >= m_columns ) {\n         throw std::out_of_range( std::format( \"column {} out of range (0-{})\", column, m_columns - 1 ) );\n      }\n      return PQfname( m_pgresult.get(), static_cast< int >( column ) );\n   }\n\n   auto result::index( const internal::zsv in_name ) const -> std::size_t\n   {\n      assert( m_columns != 0 );\n      const int column = PQfnumber( m_pgresult.get(), in_name );\n      if( column < 0 ) {\n         assert( column == -1 );\n         throw std::out_of_range( std::format( \"column '{}' not found\", in_name.value ) );\n      }\n      return column;\n   }\n\n   auto result::begin() const noexcept -> result::const_iterator\n   {\n      assert( m_columns != 0 );\n      return const_iterator( row( *this, 0, 0, m_columns ) );\n   }\n\n   auto result::end() const noexcept -> result::const_iterator\n   {\n      return const_iterator( row( *this, size(), 0, m_columns ) );\n   }\n\n   auto result::is_null( const std::size_t row, const std::size_t column ) const -> bool\n   {\n      check_row( row );\n      if( column >= m_columns ) {\n         throw std::out_of_range( std::format( \"column {} out of range (0-{})\", column, m_columns - 1 ) );\n      }\n      return PQgetisnull( m_pgresult.get(), static_cast< int >( row ), static_cast< int >( column ) ) != 0;\n   }\n\n   auto result::get( const std::size_t row, const std::size_t column ) const -> const char*\n   {\n      if( is_null( row, column ) ) {\n         throw std::runtime_error( std::format( \"unexpected NULL value in row {} column {}/'{}'\", row, column, name( column ) ) );\n      }\n      return PQgetvalue( m_pgresult.get(), static_cast< int >( row ), static_cast< int >( column ) );\n   }\n\n   auto result::at( const std::size_t row ) const -> pq::row\n   {\n      check_row( row );\n      return ( *this )[ row ];\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/result_traits.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/result_traits.hpp>\n\n#include <cstddef>\n#include <cstring>\n#include <format>\n#include <stdexcept>\n#include <string>\n\n#include <tao/pq/binary.hpp>\n#include <tao/pq/internal/from_chars.hpp>\n#include <tao/pq/internal/resize_uninitialized.hpp>\n#include <tao/pq/internal/strtox.hpp>\n\nnamespace tao::pq\n{\n   namespace\n   {\n      [[nodiscard]] auto unhex( const char c ) -> int\n      {\n         if( ( c >= '0' ) && ( c <= '9' ) ) {\n            return c - '0';\n         }\n         if( ( c >= 'a' ) && ( c <= 'f' ) ) {\n            return c - 'a' + 10;\n         }\n         throw std::invalid_argument( \"unhex failed\" );\n      }\n\n      [[nodiscard]] auto unescape_bytea( const char* value ) -> binary\n      {\n         if( ( value[ 0 ] != '\\\\' ) || ( value[ 1 ] != 'x' ) ) {\n            throw std::invalid_argument( std::format( \"unescape BYTEA failed: {}\", value ) );\n         }\n\n         const auto input = std::strlen( value );\n         if( input % 2 == 1 ) {\n            throw std::invalid_argument( std::format( \"unescape BYTEA failed: {}\", value ) );\n         }\n\n         const auto size = ( input / 2 ) - 1;\n\n         binary nrv;\n         internal::resize_uninitialized( nrv, size );\n\n         for( std::size_t pos = 0; pos < size; ++pos ) {\n            const auto high = unhex( value[ 2 + ( 2 * pos ) ] );\n            const auto low = unhex( value[ 2 + ( 2 * pos ) + 1 ] );\n            nrv[ pos ] = static_cast< std::byte >( ( high << 4 ) | low );\n         }\n         return nrv;\n      }\n\n   }  // namespace\n\n   auto result_traits< bool >::from( const char* value ) -> bool\n   {\n      if( ( value[ 0 ] != '\\0' ) && ( value[ 1 ] == '\\0' ) ) {\n         if( value[ 0 ] == 't' ) {\n            return true;\n         }\n         if( value[ 0 ] == 'f' ) {\n            return false;\n         }\n      }\n      throw std::runtime_error( std::format( \"invalid value in tao::pq::result_traits<bool> for input: {}\", value ) );\n   }\n\n   auto result_traits< char >::from( const char* value ) -> char\n   {\n      if( ( value[ 0 ] == '\\0' ) || ( value[ 1 ] != '\\0' ) ) {\n         throw std::runtime_error( std::format( \"invalid value in tao::pq::result_traits<char> for input: {}\", value ) );\n      }\n      return value[ 0 ];\n   }\n\n   auto result_traits< signed char >::from( const char* value ) -> signed char\n   {\n      return internal::from_chars< signed char >( value );\n   }\n\n   auto result_traits< unsigned char >::from( const char* value ) -> unsigned char\n   {\n      return internal::from_chars< unsigned char >( value );\n   }\n\n   auto result_traits< short >::from( const char* value ) -> short\n   {\n      return internal::from_chars< short >( value );\n   }\n\n   auto result_traits< unsigned short >::from( const char* value ) -> unsigned short\n   {\n      return internal::from_chars< unsigned short >( value );\n   }\n\n   auto result_traits< int >::from( const char* value ) -> int\n   {\n      return internal::from_chars< int >( value );\n   }\n\n   auto result_traits< unsigned >::from( const char* value ) -> unsigned\n   {\n      return internal::from_chars< unsigned >( value );\n   }\n\n   auto result_traits< long >::from( const char* value ) -> long\n   {\n      return internal::from_chars< long >( value );\n   }\n\n   auto result_traits< unsigned long >::from( const char* value ) -> unsigned long\n   {\n      return internal::from_chars< unsigned long >( value );\n   }\n\n   auto result_traits< long long >::from( const char* value ) -> long long\n   {\n      return internal::from_chars< long long >( value );\n   }\n\n   auto result_traits< unsigned long long >::from( const char* value ) -> unsigned long long\n   {\n      return internal::from_chars< unsigned long long >( value );\n   }\n\n   auto result_traits< float >::from( const char* value ) -> float\n   {\n      return internal::strtof( value );\n   }\n\n   auto result_traits< double >::from( const char* value ) -> double\n   {\n      return internal::strtod( value );\n   }\n\n   auto result_traits< long double >::from( const char* value ) -> long double\n   {\n      return internal::strtold( value );\n   }\n\n   auto result_traits< binary >::from( const char* value ) -> binary\n   {\n      return unescape_bytea( value );\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/result_traits_array.cpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/result_traits_array.hpp>\n\n#include <cstring>\n#include <stdexcept>\n#include <string>\n\n#include <tao/pq/internal/unreachable.hpp>\n\nnamespace tao::pq::internal\n{\n   auto parse_quoted( const char*& value ) -> std::string\n   {\n      std::string result;\n      while( const auto* pos = std::strpbrk( value, \"\\\\\\\"\" ) ) {\n         switch( *pos ) {\n            case '\\\\':\n               result.append( value, pos++ );\n               result += *pos++;\n               value = pos;\n               break;\n\n            case '\"':\n               result.append( value, pos++ );\n               value = pos;\n               return result;\n\n            default:\n               TAO_PQ_INTERNAL_UNREACHABLE;\n         }\n      }\n      throw std::invalid_argument( \"unterminated quoted string\" );\n   }\n\n   auto parse_unquoted( const char*& value ) -> std::string\n   {\n      if( const auto* end = std::strpbrk( value, \",;}\" ) ) {\n         const std::string result( value, end );\n         value = end;\n         return result;\n      }\n      throw std::invalid_argument( \"unterminated unquoted string\" );\n   }\n\n}  // namespace tao::pq::internal\n"
  },
  {
    "path": "src/lib/pq/row.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/row.hpp>\n\n#include <cassert>\n#include <cstddef>\n#include <format>\n#include <stdexcept>\n#include <string>\n\n#include <tao/pq/internal/zsv.hpp>\n#include <tao/pq/result.hpp>\n\nnamespace tao::pq\n{\n   void row::ensure_column( const std::size_t column ) const\n   {\n      if( column >= m_columns ) {\n         throw std::out_of_range( std::format( \"column {} out of range (0-{})\", column, m_columns - 1 ) );\n      }\n   }\n\n   auto row::slice( const std::size_t offset, const std::size_t in_columns ) const -> row\n   {\n      assert( m_result );\n      if( in_columns == 0 ) {\n         throw std::invalid_argument( \"slice requires at least one column\" );\n      }\n      if( offset + in_columns > m_columns ) {\n         throw std::out_of_range( std::format( \"slice ({}-{}) out of range (0-{})\", offset, offset + in_columns - 1, m_columns - 1 ) );\n      }\n      return { *m_result, m_row, m_offset + offset, in_columns };\n   }\n\n   auto row::name( const std::size_t column ) const -> std::string\n   {\n      assert( m_result );\n      return m_result->name( m_offset + column );\n   }\n\n   auto row::index( const internal::zsv in_name ) const -> std::size_t\n   {\n      assert( m_result );\n      const std::size_t n = m_result->index( in_name );\n      if( n >= m_offset ) {\n         if( n - m_offset < m_columns ) {\n            return n - m_offset;\n         }\n      }\n      else {\n         const std::string adapted_name = m_result->name( n );\n         for( std::size_t pos = 0; pos < m_columns; ++pos ) {\n            if( name( pos ) == adapted_name ) {\n               return pos;\n            }\n         }\n      }\n      throw std::out_of_range( std::format( \"column not found: {}\", static_cast< const char* >( in_name ) ) );\n   }\n\n   auto row::begin() const noexcept -> row::const_iterator\n   {\n      return const_iterator( field( *this, m_offset ) );\n   }\n\n   auto row::end() const noexcept -> row::const_iterator\n   {\n      return const_iterator( field( *this, m_offset + m_columns ) );\n   }\n\n   auto row::is_null( const std::size_t column ) const -> bool\n   {\n      ensure_column( column );\n      assert( m_result );\n      return m_result->is_null( m_row, m_offset + column );\n   }\n\n   auto row::get( const std::size_t column ) const -> const char*\n   {\n      ensure_column( column );\n      assert( m_result );\n      return m_result->get( m_row, m_offset + column );\n   }\n\n   auto row::at( const std::size_t column ) const -> field\n   {\n      ensure_column( column );\n      return { *this, m_offset + column };\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/table_field.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/table_field.hpp>\n\n#include <cstddef>\n\n#include <tao/pq/table_row.hpp>\n\nnamespace tao::pq\n{\n   auto table_field::index() const noexcept -> std::size_t\n   {\n      return m_column - m_row->m_offset;\n   }\n\n   auto table_field::is_null() const -> bool\n   {\n      return m_row->is_null( m_column );\n   }\n\n   auto table_field::get() const -> const char*\n   {\n      return m_row->get( m_column );\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/table_reader.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/table_reader.hpp>\n\n#include <cassert>\n#include <chrono>\n#include <cstring>\n#include <stdexcept>\n#include <string_view>\n#include <tuple>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/connection.hpp>\n#include <tao/pq/exception.hpp>\n#include <tao/pq/internal/unreachable.hpp>\n#include <tao/pq/result.hpp>\n\nnamespace tao::pq\n{\n   void table_reader::check_result()\n   {\n      const auto start = std::chrono::steady_clock::now();\n      const auto end = m_transaction->connection()->timeout_end( start );\n      auto result = m_transaction->connection()->get_result( end );\n      switch( PQresultStatus( result.get() ) ) {\n         case PGRES_COPY_OUT:\n            m_columns = PQnfields( result.get() );\n            break;\n\n         case PGRES_COPY_IN:\n            std::ignore = m_transaction->get_result( start );\n            TAO_PQ_INTERNAL_UNREACHABLE;  // LCOV_EXCL_LINE\n\n         case PGRES_COMMAND_OK:\n         case PGRES_TUPLES_OK:\n            m_transaction->connection()->consume_empty_result( end );\n            throw std::runtime_error( \"expected COPY TO statement\" );\n\n         case PGRES_EMPTY_QUERY:\n            m_transaction->connection()->consume_empty_result( end );\n            throw std::runtime_error( \"unexpected empty query\" );\n\n         default:\n            m_transaction->connection()->consume_empty_result( end );\n            internal::throw_sqlstate( result.get() );\n      }\n   }\n\n   auto table_reader::get_raw_data() -> std::string_view\n   {\n      char* buffer = nullptr;\n      const auto size = m_transaction->connection()->get_copy_data( buffer );\n      m_buffer.reset( buffer );\n\n      if( size > 0 ) {\n         return { static_cast< const char* >( buffer ), size };\n      }\n\n      const auto end = m_transaction->connection()->timeout_end();\n      std::ignore = pq::result( m_transaction->connection()->get_result( end ).release() );\n      m_transaction.reset();\n      m_previous.reset();\n      return {};\n   }\n\n   auto table_reader::parse_data() noexcept -> bool\n   {\n      m_data.clear();\n      char* read = m_buffer.get();\n      if( read == nullptr ) {\n         return false;\n      }\n      char* write = read;\n      char* begin = write;\n      while( auto* pos = std::strpbrk( read, \"\\t\\\\\\n\" ) ) {\n         if( const auto prefix_size = pos - read ) {\n            std::memmove( write, read, prefix_size );\n            write += prefix_size;\n         }\n         switch( *pos ) {\n            case '\\t':\n               m_data.emplace_back( begin );\n               *write++ = '\\0';\n               begin = write = read = ++pos;\n               break;\n\n            case '\\\\':\n               read = pos + 1;\n               switch( *read++ ) {\n                  case 'N':\n                     assert( write == begin );\n                     m_data.emplace_back( nullptr );\n                     switch( *read ) {\n                        case '\\t':\n                           begin = write = ++read;\n                           break;\n\n                        case '\\n':\n                           return true;\n\n                        default:                         // LCOV_EXCL_LINE\n                           TAO_PQ_INTERNAL_UNREACHABLE;  // LCOV_EXCL_LINE\n                     }\n                     break;\n\n                  case 'b':\n                     *write++ = '\\b';\n                     break;\n\n                  case 'f':\n                     *write++ = '\\f';\n                     break;\n\n                  case 'n':\n                     *write++ = '\\n';\n                     break;\n\n                  case 'r':\n                     *write++ = '\\r';\n                     break;\n\n                  case 't':\n                     *write++ = '\\t';\n                     break;\n\n                  case 'v':\n                     *write++ = '\\v';\n                     break;\n\n                  case '\\\\':\n                     *write++ = '\\\\';\n                     break;\n\n                  default:                         // LCOV_EXCL_LINE\n                     TAO_PQ_INTERNAL_UNREACHABLE;  // LCOV_EXCL_LINE\n               }\n               break;\n\n            case '\\n':\n               m_data.emplace_back( begin );\n               *write++ = '\\0';\n               assert( m_data.size() == columns() );\n               return true;\n\n            default:                         // LCOV_EXCL_LINE\n               TAO_PQ_INTERNAL_UNREACHABLE;  // LCOV_EXCL_LINE\n         }\n      }\n      TAO_PQ_INTERNAL_UNREACHABLE;  // LCOV_EXCL_LINE\n   }\n\n   auto table_reader::begin() -> table_reader::const_iterator\n   {\n      std::ignore = get_row();\n      return table_row( *this, 0, columns() );\n   }\n\n   auto table_reader::end() noexcept -> table_reader::const_iterator\n   {\n      return table_row( *this, 0, 0 );\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/table_row.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/table_row.hpp>\n\n#include <cstddef>\n#include <format>\n#include <stdexcept>\n\n#include <tao/pq/table_reader.hpp>\n\nnamespace tao::pq\n{\n   void table_row::ensure_column( const std::size_t column ) const\n   {\n      if( column >= m_columns ) {\n         throw std::out_of_range( std::format( \"column {} out of range (0-{})\", column, m_columns - 1 ) );\n      }\n   }\n\n   auto table_row::slice( const std::size_t offset, const std::size_t in_columns ) const -> table_row\n   {\n      if( in_columns == 0 ) {\n         throw std::invalid_argument( \"slice requires at least one column\" );\n      }\n      if( offset + in_columns > m_columns ) {\n         throw std::out_of_range( std::format( \"slice ({}-{}) out of range (0-{})\", offset, offset + in_columns - 1, m_columns - 1 ) );\n      }\n      return { *m_reader, m_offset + offset, in_columns };\n   }\n\n   auto table_row::begin() const noexcept -> table_row::const_iterator\n   {\n      return const_iterator( table_field( *this, m_offset ) );\n   }\n\n   auto table_row::end() const noexcept -> table_row::const_iterator\n   {\n      return const_iterator( table_field( *this, m_offset + m_columns ) );\n   }\n\n   auto table_row::is_null( const std::size_t column ) const -> bool\n   {\n      return get( column ) == nullptr;\n   }\n\n   auto table_row::get( const std::size_t column ) const -> const char*\n   {\n      ensure_column( column );\n      return m_reader->raw_data()[ m_offset + column ];\n   }\n\n   auto table_row::at( const std::size_t column ) const -> table_field\n   {\n      ensure_column( column );\n      return { *this, m_offset + column };\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/table_writer.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/table_writer.hpp>\n\n#include <chrono>\n#include <cstddef>\n#include <memory>\n#include <stdexcept>\n#include <string_view>\n#include <tuple>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/connection.hpp>\n#include <tao/pq/exception.hpp>\n#include <tao/pq/result.hpp>\n\nnamespace tao::pq\n{\n   table_writer::~table_writer()\n   {\n      if( m_transaction ) {\n         try {\n            std::ignore = m_transaction->get_result();\n         }\n         catch( ... ) {  // NOLINT(bugprone-empty-catch)\n         }\n      }\n   }\n\n   void table_writer::check_result()\n   {\n      const auto end = m_transaction->connection()->timeout_end();\n      const auto result = m_transaction->connection()->get_result( end );\n      switch( PQresultStatus( result.get() ) ) {\n         case PGRES_COPY_IN:\n            break;\n\n         case PGRES_COPY_OUT:\n            m_transaction->connection()->cancel();\n            m_transaction->connection()->clear_copy_data( end );\n            std::ignore = m_transaction->connection()->get_fatal_error( end );\n            m_transaction->connection()->consume_empty_result( end );\n            throw std::runtime_error( \"unexpected COPY TO statement\" );\n\n         case PGRES_COMMAND_OK:\n         case PGRES_TUPLES_OK:\n            m_transaction->connection()->consume_empty_result( end );\n            throw std::runtime_error( \"expected COPY FROM statement\" );\n\n         case PGRES_EMPTY_QUERY:\n            m_transaction->connection()->consume_empty_result( end );\n            throw std::runtime_error( \"unexpected empty query\" );\n\n         default:\n            m_transaction->connection()->consume_empty_result( end );\n            internal::throw_sqlstate( result.get() );\n      }\n   }\n\n   void table_writer::insert_raw( const std::string_view data )\n   {\n      m_transaction->connection()->put_copy_data( data.data(), data.size() );\n   }\n\n   auto table_writer::commit() -> std::size_t\n   {\n      m_transaction->connection()->put_copy_end();\n      const auto rows_affected = m_transaction->get_result().rows_affected();\n      m_transaction.reset();\n      m_previous.reset();\n      return rows_affected;\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/transaction.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/transaction.hpp>\n\n#include <chrono>\n#include <cstdio>\n#include <memory>\n#include <stdexcept>\n#include <utility>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/connection.hpp>\n#include <tao/pq/pipeline.hpp>\n\nnamespace tao::pq\n{\n   namespace internal\n   {\n      class top_level_subtransaction final\n         : public subtransaction_base\n      {\n      public:\n         explicit top_level_subtransaction( const std::shared_ptr< pq::connection >& connection )\n            : subtransaction_base( connection )\n         {\n            execute( \"START TRANSACTION\" );\n         }\n\n         ~top_level_subtransaction() override\n         {\n            if( m_connection && m_connection->attempt_rollback() ) {\n               rollback_in_dtor();\n            }\n         }\n\n         top_level_subtransaction( const top_level_subtransaction& ) = delete;\n         top_level_subtransaction( top_level_subtransaction&& ) = delete;\n         void operator=( const top_level_subtransaction& ) = delete;\n         void operator=( top_level_subtransaction&& ) = delete;\n\n      private:\n         void v_commit() override\n         {\n            execute( \"COMMIT TRANSACTION\" );\n         }\n\n         void v_rollback() override\n         {\n            execute( \"ROLLBACK TRANSACTION\" );\n         }\n      };\n\n      class nested_subtransaction final\n         : public subtransaction_base\n      {\n      public:\n         explicit nested_subtransaction( const std::shared_ptr< pq::connection >& connection )\n            : subtransaction_base( connection )\n         {\n            char buffer[ 64 ];\n            std::snprintf( buffer, 64, \"SAVEPOINT \\\"TAOPQ_%p\\\"\", static_cast< void* >( this ) );\n            execute( buffer );\n         }\n\n         ~nested_subtransaction() override\n         {\n            if( m_connection && m_connection->attempt_rollback() ) {\n               rollback_in_dtor();\n            }\n         }\n\n         nested_subtransaction( const nested_subtransaction& ) = delete;\n         nested_subtransaction( nested_subtransaction&& ) = delete;\n         void operator=( const nested_subtransaction& ) = delete;\n         void operator=( nested_subtransaction&& ) = delete;\n\n      private:\n         void v_commit() override\n         {\n            char buffer[ 64 ];\n            std::snprintf( buffer, 64, \"RELEASE SAVEPOINT \\\"TAOPQ_%p\\\"\", static_cast< void* >( this ) );\n            execute( buffer );\n         }\n\n         void v_rollback() override\n         {\n            char buffer[ 64 ];\n            std::snprintf( buffer, 64, \"ROLLBACK TO \\\"TAOPQ_%p\\\"\", static_cast< void* >( this ) );\n            execute( buffer );\n         }\n      };\n\n   }  // namespace internal\n\n   auto transaction::subtransaction() -> std::shared_ptr< transaction >\n   {\n      check_current_transaction();\n      if( v_is_direct() ) {\n         return std::make_shared< internal::top_level_subtransaction >( m_connection );\n      }\n      return std::make_shared< internal::nested_subtransaction >( m_connection );\n   }\n\n   auto transaction::pipeline() -> std::shared_ptr< pq::pipeline >\n   {\n      check_current_transaction();\n      return std::make_shared< pq::pipeline >( pq::pipeline::private_key(), m_connection );\n   }\n\n   void transaction::commit()\n   {\n      try {\n         check_current_transaction();\n         v_commit();\n      }\n      // LCOV_EXCL_START\n      catch( ... ) {\n         v_reset();\n         throw;\n      }\n      // LCOV_EXCL_STOP\n      v_reset();\n   }\n\n   void transaction::rollback()\n   {\n      try {\n         check_current_transaction();\n         v_rollback();\n      }\n      // LCOV_EXCL_START\n      catch( ... ) {\n         v_reset();\n         throw;\n      }\n      // LCOV_EXCL_STOP\n      v_reset();\n   }\n\n   void transaction::rollback_in_dtor() noexcept\n   {\n      try {\n         check_current_transaction();\n         v_rollback();\n      }\n      // LCOV_EXCL_START\n      catch( ... ) {\n         if( m_connection->m_log && m_connection->m_log->transaction.destructor_rollback_failed ) {\n            m_connection->m_log->transaction.destructor_rollback_failed( *this );\n         }\n      }\n      // LCOV_EXCL_STOP\n      v_reset();\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "src/lib/pq/transaction_base.cpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <tao/pq/transaction_base.hpp>\n\n#include <chrono>\n#include <cstdio>\n#include <format>\n#include <memory>\n#include <stdexcept>\n#include <utility>\n\n#include <libpq-fe.h>\n\n#include <tao/pq/connection.hpp>\n#include <tao/pq/result.hpp>\n\nnamespace tao::pq\n{\n   transaction_base::transaction_base( const std::shared_ptr< pq::connection >& connection ) noexcept  // NOLINT(modernize-pass-by-value)\n      : m_connection( connection )\n   {}\n\n   auto transaction_base::current_transaction() const noexcept -> transaction_base*&\n   {\n      return m_connection->m_current_transaction;\n   }\n\n   void transaction_base::check_current_transaction() const\n   {\n      if( !m_connection || this != current_transaction() ) {\n         throw std::logic_error( \"invalid transaction order\" );\n      }\n   }\n\n   void transaction_base::send_params( const char* statement,\n                                       const int n_params,\n                                       const Oid types[],\n                                       const char* const values[],\n                                       const int lengths[],\n                                       const int formats[] )\n   {\n      check_current_transaction();\n      m_connection->send_params( statement, n_params, types, values, lengths, formats );\n   }\n\n   void transaction_base::set_single_row_mode()\n   {\n      check_current_transaction();\n      if( PQsetSingleRowMode( m_connection->underlying_raw_ptr() ) == 0 ) {\n         throw std::runtime_error( \"unable to switch to single row mode\" );\n      }\n   }\n\n#if defined( LIBPQ_HAS_CHUNK_MODE )\n   void transaction_base::set_chunk_mode( const int rows )\n   {\n      check_current_transaction();\n      if( PQsetChunkedRowsMode( m_connection->underlying_raw_ptr(), rows ) == 0 ) {\n         throw std::runtime_error( \"unable to switch to chunk mode\" );\n      }\n   }\n#endif\n\n   auto transaction_base::get_result( const std::chrono::steady_clock::time_point start ) -> result\n   {\n      check_current_transaction();\n      const auto end = m_connection->timeout_end( start );\n\n      auto result = m_connection->get_result( end );\n      if( !result ) {\n         throw std::runtime_error( \"unable to obtain result\" );\n      }\n\n      switch( PQresultStatus( result.get() ) ) {\n         case PGRES_COPY_IN:\n            m_connection->put_copy_end( \"unexpected COPY FROM statement\" );\n            result = m_connection->get_fatal_error( end );\n            break;\n\n         case PGRES_COPY_OUT:\n            m_connection->cancel();\n            m_connection->clear_copy_data( end );\n            std::ignore = m_connection->get_fatal_error( end );\n            m_connection->consume_empty_result( end );\n            throw std::runtime_error( \"unexpected COPY TO statement\" );\n\n         case PGRES_SINGLE_TUPLE:\n#if defined( LIBPQ_HAS_CHUNK_MODE )\n         case PGRES_TUPLES_CHUNK:\n#endif\n            return pq::result( result.release() );\n\n         default:;\n      }\n\n      m_connection->consume_empty_result( end );\n      return pq::result( result.release() );\n   }\n\n   void transaction_base::consume_pipeline_sync( const std::chrono::steady_clock::time_point start )\n   {\n      check_current_transaction();\n      const auto end = m_connection->timeout_end( start );\n\n      const auto result = m_connection->get_result( end );\n      if( !result ) {\n         throw std::runtime_error( \"unable to obtain result\" );\n      }\n\n      const auto status = PQresultStatus( result.get() );\n      if( status != PGRES_PIPELINE_SYNC ) {\n         throw std::runtime_error( std::format( \"unexpected result status: {}\", PQresStatus( status ) ) );\n      }\n   }\n\n}  // namespace tao::pq\n"
  },
  {
    "path": "test/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\n\noption(BUILD_INTEGRATION_TESTS \"Build integration tests that require a live PostgreSQL database\" ON)\noption(BUILD_UNIT_TESTS \"Build unit tests without needing a live database\" ON)\n\ninclude(CTest)\n\nset(SOURCE_INTEGRATION_TESTS\n  integration/aggregate.cpp\n  integration/array.cpp\n  integration/basic_datatypes.cpp\n  integration/chunk_mode.cpp\n  integration/connection.cpp\n  integration/connection_pool.cpp\n  integration/example.cpp\n  integration/exception.cpp\n  integration/large_object.cpp\n  integration/log.cpp\n  integration/notifications.cpp\n  integration/parameter.cpp\n  integration/password.cpp\n  integration/pipeline_mode.cpp\n  integration/result.cpp\n  integration/row.cpp\n  integration/single_row_mode.cpp\n  integration/table_reader.cpp\n  integration/table_writer.cpp\n  integration/traits.cpp\n  integration/transaction.cpp\n)\n\nset(SOURCE_UNIT_TESTS\n  unit/getenv.cpp\n  unit/parameter_type.cpp\n  unit/resize_uninitialized.cpp\n  unit/result_type.cpp\n  unit/strtox.cpp\n)\n\nfunction(add_taopq_test source_file)\n  get_filename_component(test_name \"${source_file}\" NAME_WE)\n  add_executable(\"${test_name}\" \"${source_file}\")\n  target_link_libraries(\"${test_name}\" PRIVATE taocpp::taopq)\n  target_compile_features(\"${test_name}\" PRIVATE cxx_std_20)\n  target_include_directories(\"${test_name}\" PRIVATE \"${CMAKE_CURRENT_SOURCE_DIR}\")\n  if(WIN32)\n    target_link_libraries(\"${test_name}\" PRIVATE wsock32 ws2_32)\n  endif()\n  add_test(\n    NAME \"${test_name}\"\n    WORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/../..\"\n    COMMAND \"$<TARGET_FILE:${test_name}>\"\n  )\nendfunction()\n\nforeach(cat IN ITEMS INTEGRATION UNIT)\n  if(BUILD_${cat}_TESTS)\n    foreach(src IN LISTS SOURCE_${cat}_TESTS)\n      add_taopq_test(\"${CMAKE_CURRENT_SOURCE_DIR}/${src}\")\n    endforeach()\n  endif()\nendforeach()\n"
  },
  {
    "path": "test/integration/aggregate.cpp",
    "content": "// Copyright (c) 2020-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <iostream>\n#include <string>\n\n#include <tao/pq.hpp>\n\nnamespace example\n{\n   struct user\n   {\n      std::string name;\n      int age;\n      std::string planet;\n   };\n\n}  // namespace example\n\ntemplate<>\ninline constexpr bool tao::pq::is_aggregate< example::user > = true;\n\nnamespace\n{\n   void run()\n   {\n      const auto connection = tao::pq::connection::create( tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" ) );\n\n      connection->execute( \"DROP TABLE IF EXISTS tao_aggregate_test\" );\n      connection->execute( \"CREATE TABLE tao_aggregate_test ( name TEXT PRIMARY KEY, age INTEGER, planet TEXT )\" );\n\n      {\n         const example::user u{\n            .name = \"R. Giskard Reventlov\",\n            .age = 42,\n            .planet = \"Aurora\"\n         };\n         TEST_EXECUTE( connection->execute( \"INSERT INTO tao_aggregate_test VALUES ( $1, $2, $3 )\", u ) );\n      }\n\n      for( const example::user u : connection->execute( \"SELECT name, age, planet FROM tao_aggregate_test\" ) ) {  // NOLINT(performance-for-range-copy)\n         TEST_ASSERT( u.name == \"R. Giskard Reventlov\" );\n         TEST_ASSERT( u.age == 42 );\n         TEST_ASSERT( u.planet == \"Aurora\" );\n      }\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/array.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <array>\n#include <exception>\n#include <iostream>\n#include <optional>\n#include <set>\n#include <span>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   void run()\n   {\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );\n      const auto connection = tao::pq::connection::create( connection_string );\n\n      {\n         connection->execute( \"DROP TABLE IF EXISTS tao_array_test\" );\n         connection->execute( \"CREATE TABLE tao_array_test ( a INTEGER[] )\" );\n\n         const std::array v{ 1, 0, 2, 4 };\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", v );\n\n         std::vector v2 = { 42, 1701 };\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", v2 );\n\n         v2.clear();\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", v2 );\n\n         const auto r = connection->execute( \"SELECT * FROM tao_array_test\" ).vector< std::vector< int > >();\n         TEST_ASSERT( r.size() == 3 );\n         TEST_ASSERT( r[ 0 ].size() == 4 );\n         TEST_ASSERT( r[ 0 ][ 0 ] == 1 );\n         TEST_ASSERT( r[ 0 ][ 1 ] == 0 );\n         TEST_ASSERT( r[ 0 ][ 2 ] == 2 );\n         TEST_ASSERT( r[ 0 ][ 3 ] == 4 );\n         TEST_ASSERT( r[ 1 ].size() == 2 );\n         TEST_ASSERT( r[ 1 ][ 0 ] == 42 );\n         TEST_ASSERT( r[ 1 ][ 1 ] == 1701 );\n         TEST_ASSERT( r[ 2 ].empty() );\n      }\n\n      {\n         connection->execute( \"DROP TABLE IF EXISTS tao_array_test\" );\n         connection->execute( \"CREATE TABLE tao_array_test ( a TEXT[] )\" );\n\n         using type = std::vector< std::optional< std::string > >;\n         const type v = { \"FOO\", \"\", \"{BAR\\\\BAZ\\\"B,L;A}\", \"NULL\", std::nullopt };\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", v );\n\n         const auto r = connection->execute( \"SELECT * FROM tao_array_test\" ).as< type >();\n         TEST_ASSERT( r.size() == 5 );\n         TEST_ASSERT( r[ 0 ] == \"FOO\" );\n         TEST_ASSERT( r[ 1 ]->empty() );  // NOLINT(bugprone-unchecked-optional-access)\n         TEST_ASSERT( r[ 2 ] == \"{BAR\\\\BAZ\\\"B,L;A}\" );\n         TEST_ASSERT( r[ 3 ] == \"NULL\" );\n         TEST_ASSERT( !r[ 4 ] );\n      }\n\n      {\n         connection->execute( \"DROP TABLE IF EXISTS tao_array_test\" );\n         connection->execute( \"CREATE TABLE tao_array_test ( a TEXT[] )\" );\n\n         using type = std::vector< std::string >;\n         const type v = { \"FOO\", \"\", \"{BAR\\\\BAZ\\\"B,L;A}\", \"NULL\" };\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", std::span{ v } );\n\n         const auto r = connection->execute( \"SELECT * FROM tao_array_test\" ).as< type >();\n         TEST_ASSERT( r.size() == 4 );\n         TEST_ASSERT( r[ 0 ] == \"FOO\" );\n         TEST_ASSERT( r[ 1 ].empty() );  // NOLINT(bugprone-unchecked-optional-access)\n         TEST_ASSERT( r[ 2 ] == \"{BAR\\\\BAZ\\\"B,L;A}\" );\n         TEST_ASSERT( r[ 3 ] == \"NULL\" );\n      }\n\n      {\n         connection->execute( \"DROP TABLE IF EXISTS tao_array_test\" );\n         connection->execute( \"CREATE TABLE tao_array_test ( a TEXT[][] NOT NULL )\" );\n\n         using type = std::vector< std::set< std::string > >;\n         const type v = { { \"1\", \"F\\\"O\\\\O\", \"NULL\" }, { \"4\", \" XYZ \", \"6\" } };\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", v );\n\n         const auto r = connection->execute( \"SELECT * FROM tao_array_test\" ).as< type >();\n         TEST_ASSERT( r == v );\n      }\n\n      {\n         connection->execute( \"DROP TABLE IF EXISTS tao_array_test\" );\n         connection->execute( \"CREATE TABLE tao_array_test ( a TEXT[][] NOT NULL )\" );\n\n         using type = std::vector< std::set< std::tuple< std::string > > >;\n         const type v = { { \"1\", \"F\\\"O\\\\O\", \"NULL\" }, { \"4\", \" XYZ \", \"6\" } };\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", v );\n\n         const auto r = connection->execute( \"SELECT * FROM tao_array_test\" ).as< type >();\n         TEST_ASSERT( r == v );\n      }\n\n      {\n         connection->execute( \"DROP TABLE IF EXISTS tao_array_test\" );\n         connection->execute( \"CREATE TABLE tao_array_test ( a BYTEA[][] NOT NULL )\" );\n\n         using type = std::vector< tao::pq::binary >;\n         const type v = { tao::pq::to_binary( \"1\" ),\n                          tao::pq::binary(),\n                          tao::pq::to_binary( \"F\\\"O\\\\O\" ),\n                          tao::pq::to_binary( \"NU\\0LL\" ) };\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", v );\n\n         const auto r = connection->execute( \"SELECT * FROM tao_array_test\" ).as< type >();\n         TEST_ASSERT( r == v );\n      }\n\n      TEST_THROWS( connection->execute( \"SELECT $1\", \"\" ).as< std::vector< std::string > >() );\n      TEST_THROWS( connection->execute( \"SELECT $1\", \"{\" ).as< std::vector< std::string > >() );\n      TEST_THROWS( connection->execute( \"SELECT $1\", \"{FOO\" ).as< std::vector< std::string > >() );\n      TEST_THROWS( connection->execute( \"SELECT $1\", \"{NULL}\" ).as< std::vector< std::string > >() );\n      TEST_THROWS( connection->execute( \"SELECT $1\", \"{\\\"FOO}\" ).as< std::vector< std::string > >() );\n      TEST_THROWS( connection->execute( \"SELECT $1\", \"{FOO}BAR\" ).as< std::vector< std::string > >() );\n\n      {\n         connection->execute( \"DROP TABLE IF EXISTS tao_array_test\" );\n         connection->execute( \"CREATE TABLE tao_array_test ( a INTEGER NOT NULL )\" );\n\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", 1 );\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", 2 );\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", 3 );\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", 4 );\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", 5 );\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", 6 );\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", 7 );\n         connection->execute( \"INSERT INTO tao_array_test VALUES ( $1 )\", 8 );\n\n         const auto result = connection->execute( \"SELECT * FROM tao_array_test WHERE a = ANY( $1 )\", std::array{ 2, 3, 5, 6, 9 } );\n         TEST_ASSERT( result.vector< int >() == std::vector< int >{ 2, 3, 5, 6 } );\n      }\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/basic_datatypes.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <chrono>\n#include <cmath>\n#include <exception>\n#include <iostream>\n#include <limits>\n#include <memory>\n#include <string>\n\n#include \"utils/compare.hpp\"\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   std::shared_ptr< tao::pq::connection > my_connection;\n\n   auto prepare_datatype( const std::string& datatype ) -> bool\n   {\n      static std::string last;\n      if( datatype == last ) {\n         return false;\n      }\n      last = datatype;\n      my_connection->execute( \"DROP TABLE IF EXISTS tao_basic_datatypes_test\" );\n      my_connection->execute( \"CREATE TABLE tao_basic_datatypes_test ( a \" + datatype + \" )\" );\n      return true;\n   }\n\n   void check_null( const std::string& datatype )\n   {\n      std::cout << \"check null: \" << datatype << '\\n';\n      if( prepare_datatype( datatype ) ) {\n         TEST_ASSERT( my_connection->execute( \"INSERT INTO tao_basic_datatypes_test VALUES ( $1 )\", tao::pq::null ).rows_affected() == 1 );\n      }\n      else {\n         TEST_ASSERT( my_connection->execute( \"UPDATE tao_basic_datatypes_test SET a=$1\", tao::pq::null ).rows_affected() == 1 );\n      }\n      const auto result = my_connection->execute( \"SELECT * FROM tao_basic_datatypes_test\" );\n      TEST_ASSERT( result[ 0 ][ 0 ].is_null() );\n   }\n\n   template< typename T >\n   void check( const std::string& datatype, const T& value )\n   {\n      std::cout << \"check: \" << datatype << \" value: \" << value << '\\n';\n      TEST_ASSERT( !prepare_datatype( datatype ) );\n      TEST_ASSERT( my_connection->execute( \"UPDATE tao_basic_datatypes_test SET a=$1\", value ).rows_affected() == 1 );\n\n      const auto result = my_connection->execute( \"SELECT * FROM tao_basic_datatypes_test\" );\n      if( value == value ) {  // NOLINT(misc-redundant-expression)\n         if( result[ 0 ][ 0 ].as< T >() != value ) {\n            // LCOV_EXCL_START\n            std::cout << \"check: \" << datatype << \" value: \" << value << \" result: \" << result.get( 0, 0 ) << \" FAILED!\\n\";\n            TEST_ASSERT( false );\n            // LCOV_EXCL_STOP\n         }\n      }\n      else {\n         const auto v = result[ 0 ][ 0 ].as< T >();\n         if( v == v ) {  // NOLINT(misc-redundant-expression)\n            // LCOV_EXCL_START\n            std::cout << \"check: \" << datatype << \" value: NaN result: \" << result.get( 0, 0 ) << \" FAILED!\\n\";\n            TEST_ASSERT( false );\n            // LCOV_EXCL_STOP\n         }\n      }\n   }\n\n   template< typename T >\n      requires std::is_signed_v< T >\n   auto check( const std::string& datatype )\n   {\n      check_null( datatype );\n      check< T >( datatype, std::numeric_limits< T >::min() );\n      check_null( datatype );\n      check< T >( datatype, -42 );\n      check< T >( datatype, -1 );\n      check< T >( datatype, 0 );\n      check< T >( datatype, 1 );\n      check< T >( datatype, 42 );\n      check< T >( datatype, std::numeric_limits< T >::max() );\n   }\n\n   template< typename T >\n      requires std::is_unsigned_v< T >\n   auto check( const std::string& datatype )\n   {\n      check_null( datatype );\n      check< T >( datatype, 0 );\n      check_null( datatype );\n      check< T >( datatype, 1 );\n      check< T >( datatype, 42 );\n      check< T >( datatype, std::numeric_limits< T >::max() );\n   }\n\n   void check_bytea( const auto& t )\n   {\n      TEST_ASSERT( my_connection->execute( \"UPDATE tao_basic_datatypes_test SET a=$1\", t ).rows_affected() == 1 );\n\n      const auto result = my_connection->execute( \"SELECT * FROM tao_basic_datatypes_test\" )[ 0 ][ 0 ].as< tao::pq::binary >();\n      TEST_ASSERT( tao::pq::internal::compare( result, t ) );\n   }\n\n   void run()\n   {\n      my_connection = tao::pq::connection::create( tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" ) );\n      my_connection->set_timeout( std::chrono::seconds( 1 ) );\n\n      check_null( \"BOOLEAN\" );\n      check< bool >( \"BOOLEAN\", true );\n      check_null( \"BOOLEAN\" );\n      check< bool >( \"BOOLEAN\", false );\n\n      // single characters in PostgreSQL are stored in a column of type \"char\", note the quotes are part of the type's name!\n      check_null( \"\\\"char\\\"\" );\n      check< char >( \"\\\"char\\\"\", 'a' );\n      check_null( \"\\\"char\\\"\" );\n      check< char >( \"\\\"char\\\"\", 'z' );\n      check< char >( \"\\\"char\\\"\", 'A' );\n      check< char >( \"\\\"char\\\"\", 'Z' );\n      check< char >( \"\\\"char\\\"\", '0' );\n      check< char >( \"\\\"char\\\"\", '9' );\n      check< char >( \"\\\"char\\\"\", '$' );\n      check< char >( \"\\\"char\\\"\", '%' );\n      check< char >( \"\\\"char\\\"\", ' ' );\n      check< char >( \"\\\"char\\\"\", '\"' );\n      check< char >( \"\\\"char\\\"\", '\\'' );\n      check< char >( \"\\\"char\\\"\", '\\\\' );\n      check< char >( \"\\\"char\\\"\", '\\n' );\n      check< char >( \"\\\"char\\\"\", '\\t' );\n\n      check< signed char >( \"NUMERIC\" );\n      check< unsigned char >( \"NUMERIC\" );\n      check< short >( \"NUMERIC\" );\n      check< unsigned short >( \"NUMERIC\" );\n      check< int >( \"NUMERIC\" );\n      check< unsigned >( \"NUMERIC\" );\n      check< long >( \"NUMERIC\" );\n      check< unsigned long >( \"NUMERIC\" );\n      check< long long >( \"NUMERIC\" );\n      check< unsigned long long >( \"NUMERIC\" );\n\n      // this allows precise floating point values\n      my_connection->execute( \"SET extra_float_digits=3\" );\n\n      check_null( \"REAL\" );\n      check< float >( \"REAL\", std::numeric_limits< float >::lowest() );\n      check_null( \"REAL\" );\n      check< float >( \"REAL\", -1e37F );\n      check< float >( \"REAL\", -1.25F );\n      check< float >( \"REAL\", -1.F );\n      check< float >( \"REAL\", -0.25F );\n      check< float >( \"REAL\", -1e-37F );\n      check< float >( \"REAL\", 0.F );\n      check< float >( \"REAL\", std::numeric_limits< float >::min() );\n      check< float >( \"REAL\", 1e-37F );\n      {\n         float value = 0.123456F;\n         for( int i = 0; i < 32; ++i ) {\n            check< float >( \"REAL\", value );\n            value = std::nextafterf( value, 1 );\n         }\n      }\n      check< float >( \"REAL\", 0.25F );\n      check< float >( \"REAL\", 1.F );\n      check< float >( \"REAL\", 1.F + std::numeric_limits< float >::epsilon() );\n      check< float >( \"REAL\", 1.25F );\n      check< float >( \"REAL\", 1e37F );\n      check< float >( \"REAL\", std::numeric_limits< float >::max() );\n      check< float >( \"REAL\", INFINITY );\n      check< float >( \"REAL\", -INFINITY );\n      check< float >( \"REAL\", NAN );\n\n      check_null( \"DOUBLE PRECISION\" );\n      check< double >( \"DOUBLE PRECISION\", std::numeric_limits< double >::lowest() );\n      check_null( \"DOUBLE PRECISION\" );\n      check< double >( \"DOUBLE PRECISION\", -1e308 );\n      check< double >( \"DOUBLE PRECISION\", -1.25 );\n      check< double >( \"DOUBLE PRECISION\", -1 );\n      check< double >( \"DOUBLE PRECISION\", -0.25 );\n      check< double >( \"DOUBLE PRECISION\", -1e-307 );\n      check< double >( \"DOUBLE PRECISION\", 0 );\n      check< double >( \"DOUBLE PRECISION\", std::numeric_limits< double >::min() );\n      check< double >( \"DOUBLE PRECISION\", 1e-307 );\n      {\n         double value = 0.123456789012345;\n         for( int i = 0; i < 32; ++i ) {\n            check< double >( \"DOUBLE PRECISION\", value );\n            value = std::nextafter( value, 1 );\n         }\n      }\n      check< double >( \"DOUBLE PRECISION\", 0.25 );\n      check< double >( \"DOUBLE PRECISION\", 1 );\n      check< double >( \"DOUBLE PRECISION\", 1 + std::numeric_limits< double >::epsilon() );\n      check< double >( \"DOUBLE PRECISION\", 1.25 );\n      check< double >( \"DOUBLE PRECISION\", 1e308 );\n      check< double >( \"DOUBLE PRECISION\", std::numeric_limits< double >::max() );\n      check< double >( \"DOUBLE PRECISION\", INFINITY );\n      check< double >( \"DOUBLE PRECISION\", -INFINITY );  // NOLINT(bugprone-narrowing-conversions)\n      check< double >( \"DOUBLE PRECISION\", NAN );\n\n      check_null( \"NUMERIC\" );\n      check< float >( \"NUMERIC\", std::numeric_limits< float >::lowest() );\n      check< float >( \"NUMERIC\", -1e37F );\n      check< float >( \"NUMERIC\", -1.25F );\n      check< float >( \"NUMERIC\", -1.F );\n      check< float >( \"NUMERIC\", -0.25F );\n      check< float >( \"NUMERIC\", -1e-37F );\n      check< float >( \"NUMERIC\", 0.F );\n      // check< float >( \"NUMERIC\", std::numeric_limits< float >::min() );\n      check< float >( \"NUMERIC\", 1e-37F );\n      {\n         float value = 0.123456F;\n         for( int i = 0; i < 32; ++i ) {\n            check< float >( \"NUMERIC\", value );\n            value = std::nextafterf( value, 1 );\n         }\n      }\n      check< float >( \"NUMERIC\", 0.25F );\n      check< float >( \"NUMERIC\", 1.F );\n      check< float >( \"NUMERIC\", 1.F + std::numeric_limits< float >::epsilon() );\n      check< float >( \"NUMERIC\", 1.25F );\n      check< float >( \"NUMERIC\", 1e37F );\n      check< float >( \"NUMERIC\", std::numeric_limits< float >::max() );\n      check< float >( \"NUMERIC\", NAN );\n\n      check< double >( \"NUMERIC\", std::numeric_limits< double >::lowest() );\n      check< double >( \"NUMERIC\", -1e308 );\n      check< double >( \"NUMERIC\", -1.25 );\n      check< double >( \"NUMERIC\", -1 );\n      check< double >( \"NUMERIC\", -0.25 );\n      check< double >( \"NUMERIC\", -1e-307 );\n      check< double >( \"NUMERIC\", 0 );\n      check< double >( \"NUMERIC\", std::numeric_limits< double >::min() );\n      check< double >( \"NUMERIC\", 1e-307 );\n      {\n         double value = 0.123456789012345;\n         for( int i = 0; i < 32; ++i ) {\n            check< double >( \"NUMERIC\", value );\n            value = std::nextafter( value, 1 );\n         }\n      }\n      check< double >( \"NUMERIC\", 0.25 );\n      check< double >( \"NUMERIC\", 1 );\n      check< double >( \"NUMERIC\", 1 + std::numeric_limits< double >::epsilon() );\n      check< double >( \"NUMERIC\", 1.25 );\n      check< double >( \"NUMERIC\", 1e308 );\n      check< double >( \"NUMERIC\", std::numeric_limits< double >::max() );\n      check< double >( \"NUMERIC\", NAN );\n\n      // check< long double >( \"NUMERIC\", std::numeric_limits< long double >::lowest() );\n      check< long double >( \"NUMERIC\", -1e308L );\n      check< long double >( \"NUMERIC\", -1.25L );\n      check< long double >( \"NUMERIC\", -1L );\n      check< long double >( \"NUMERIC\", -0.25L );\n      check< long double >( \"NUMERIC\", -1e-307L );\n      check< long double >( \"NUMERIC\", 0 );\n      // check< long double >( \"NUMERIC\", std::numeric_limits< long double >::min() );\n      check< long double >( \"NUMERIC\", 1e-307L );\n      // {\n      //    long double value = 0.123456789012345L;\n      //    for( int i = 0; i < 32; ++i ) {\n      //       check< long double >( \"NUMERIC\", value );\n      //       value = std::nextafterl( value, 1 );\n      //    }\n      // }\n      check< long double >( \"NUMERIC\", 0.25L );\n      check< long double >( \"NUMERIC\", 1L );\n      check< long double >( \"NUMERIC\", 1e-307L );\n      // check< long double >( \"NUMERIC\", 1.L + std::numeric_limits< long double >::epsilon() );\n      check< long double >( \"NUMERIC\", 1.25L );\n      check< long double >( \"NUMERIC\", 1e308L );\n      // check< long double >( \"NUMERIC\", std::numeric_limits< long double >::max() );\n      check< long double >( \"NUMERIC\", NAN );\n\n      check_null( \"TEXT\" );\n      check< float >( \"TEXT\", std::numeric_limits< float >::lowest() );\n      check_null( \"TEXT\" );\n      check< float >( \"TEXT\", -1e37F );\n      check< float >( \"TEXT\", -1.25F );\n      check< float >( \"TEXT\", -1.F );\n      check< float >( \"TEXT\", -0.25F );\n      check< float >( \"TEXT\", -1e-37F );\n      check< float >( \"TEXT\", 0.F );\n      // check< float >( \"TEXT\", std::numeric_limits< float >::min() );\n      check< float >( \"TEXT\", 1e-37F );\n      {\n         float value = 0.123456F;\n         for( int i = 0; i < 32; ++i ) {\n            check< float >( \"TEXT\", value );\n            value = std::nextafterf( value, 1 );\n         }\n      }\n      check< float >( \"TEXT\", 0.25F );\n      check< float >( \"TEXT\", 1.F );\n      check< float >( \"TEXT\", 1.F + std::numeric_limits< float >::epsilon() );\n      check< float >( \"TEXT\", 1.25F );\n      check< float >( \"TEXT\", 1e37F );\n      check< float >( \"TEXT\", std::numeric_limits< float >::max() );\n      check< float >( \"TEXT\", INFINITY );\n      check< float >( \"TEXT\", -INFINITY );\n      check< float >( \"TEXT\", NAN );\n\n      check< double >( \"TEXT\", std::numeric_limits< double >::lowest() );\n      check< double >( \"TEXT\", -1e308 );\n      check< double >( \"TEXT\", -1.25 );\n      check< double >( \"TEXT\", -1 );\n      check< double >( \"TEXT\", -0.25 );\n      check< double >( \"TEXT\", -1e-307 );\n      check< double >( \"TEXT\", 0 );\n      check< double >( \"TEXT\", std::numeric_limits< double >::min() );\n      check< double >( \"TEXT\", 1e-307 );\n      {\n         double value = 0.123456789012345;\n         for( int i = 0; i < 32; ++i ) {\n            check< double >( \"TEXT\", value );\n            value = std::nextafter( value, 1 );\n         }\n      }\n      check< double >( \"TEXT\", 0.25 );\n      check< double >( \"TEXT\", 1 );\n      check< double >( \"TEXT\", 1 + std::numeric_limits< double >::epsilon() );\n      check< double >( \"TEXT\", 1.25 );\n      check< double >( \"TEXT\", 1e308 );\n      check< double >( \"TEXT\", std::numeric_limits< double >::max() );\n      check< double >( \"TEXT\", INFINITY );\n      check< double >( \"TEXT\", -INFINITY );  // NOLINT(bugprone-narrowing-conversions)\n      check< double >( \"TEXT\", NAN );\n\n      // there is no data type to store 'long double' to PostgreSQL - but TEXT should do just fine...\n      // check< long double >( \"TEXT\", std::numeric_limits< long double >::lowest() );\n      check< long double >( \"TEXT\", -1e308L );\n      check< long double >( \"TEXT\", -1.25L );\n      check< long double >( \"TEXT\", -1.L );\n      check< long double >( \"TEXT\", -0.25L );\n      check< long double >( \"TEXT\", -1e-307L );\n      check< long double >( \"TEXT\", 0 );\n      // check< long double >( \"TEXT\", std::numeric_limits< long double >::min() );\n      check< long double >( \"TEXT\", 1e-307L );\n      // {\n      //    long double value = 0.123456789012345L;\n      //    for( int i = 0; i < 32; ++i ) {\n      //       check< long double >( \"TEXT\", value );\n      //       value = std::nextafterl( value, 1 );\n      //    }\n      // }\n      check< long double >( \"TEXT\", 0.25L );\n      check< long double >( \"TEXT\", 1.L );\n      check< long double >( \"TEXT\", 1e-307L );\n      // check< long double >( \"TEXT\", 1.L + std::numeric_limits< long double >::epsilon() );\n      check< long double >( \"TEXT\", 1.25L );\n      check< long double >( \"TEXT\", 1e308L );\n      // check< long double >( \"TEXT\", std::numeric_limits< long double >::max() );\n      check< long double >( \"TEXT\", INFINITY );\n      check< long double >( \"TEXT\", -INFINITY );  // NOLINT(bugprone-narrowing-conversions)\n      check< long double >( \"TEXT\", NAN );\n\n      check< std::string >( \"TEXT\", \"\" );\n      check< std::string >( \"TEXT\", \" \" );\n      check< std::string >( \"TEXT\", \"abc\" );\n      check< std::string >( \"TEXT\", \"Hello, world!\" );\n      check< std::string >( \"TEXT\", \";\" );\n      check< std::string >( \"TEXT\", \"\\\\\" );\n      check< std::string >( \"TEXT\", \"\\\"\" );\n      check< std::string >( \"TEXT\", \"'\" );\n      check< std::string >( \"TEXT\", \"'; DROP TABLE users; --\" );\n      check< std::string >( \"TEXT\", \"ä\" );\n      check< std::string >( \"TEXT\", \"€\" );\n      check< std::string >( \"TEXT\", \"𝄞\" );\n      check< std::string >( \"TEXT\", \"äöüÄÖÜß€𝄞\" );\n      check< std::string >( \"TEXT\", \"ä\\tö\\nü\\1Ä\\\"Ö;Ü'ß#€𝄞\" );\n\n      check_null( \"BYTEA\" );\n\n      const unsigned char bdata[] = { 'v', 255, 0, 'a', 1, 'b', 0 };\n\n      check_bytea( tao::pq::to_binary( bdata ) );\n      check_bytea( tao::pq::to_binary_view( bdata ) );\n   }\n\n}  // namespace\n\nauto main() -> int\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/chunk_mode.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <iostream>\n#include <string>\n\n#include <tao/pq.hpp>\n\n#if !defined( LIBPQ_HAS_CHUNK_MODE )\n\nauto main() -> int\n{\n   return 0;\n}\n\n#else\n\nnamespace\n{\n   void run()\n   {\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );\n\n      // open a connection to the database\n      const auto conn = tao::pq::connection::create( connection_string );\n\n      conn->execute( \"DROP TABLE IF EXISTS tao_chunk_mode\" );\n      conn->execute( \"CREATE TABLE tao_chunk_mode ( name TEXT PRIMARY KEY, age INTEGER NOT NULL )\" );\n\n      conn->prepare( \"insert_user\", \"INSERT INTO tao_chunk_mode ( name, age ) VALUES ( $1, $2 )\" );\n      conn->execute( \"insert_user\", \"Daniel\", 42 );\n      conn->execute( \"insert_user\", \"Tom\", 41 );\n      conn->execute( \"insert_user\", \"Jerry\", 29 );\n      conn->execute( \"insert_user\", \"Alice\", 32 );\n      conn->execute( \"insert_user\", \"Bob\", 19 );\n      conn->execute( \"insert_user\", \"Charlie\", 45 );\n\n      std::size_t count = 0;\n\n      const auto tr = conn->transaction();\n      tr->send( \"SELECT name, age FROM tao_chunk_mode\" );\n      tr->set_chunk_mode( 2 );\n\n      while( true ) {\n         const auto result = tr->get_result();\n         if( result.empty() ) {\n            break;\n         }\n\n         for( const auto& row : result ) {\n            ++count;\n            std::cout << row[ \"name\" ].as< std::string >() << \" is \"\n                      << row[ \"age\" ].as< unsigned >() << \" years old.\\n\";\n         }\n      }\n\n      TEST_ASSERT( count == 6 );\n\n      count = 0;\n      tr->send( \"SELECT name, age FROM tao_chunk_mode\" );\n      tr->set_chunk_mode( 4 );\n\n      while( true ) {\n         const auto result = tr->get_result();\n         if( result.empty() ) {\n            break;\n         }\n\n         for( const auto& row : result ) {\n            ++count;\n            std::cout << row[ \"name\" ].as< std::string >() << \" is \"\n                      << row[ \"age\" ].as< unsigned >() << \" years old.\\n\";\n         }\n      }\n\n      TEST_ASSERT( count == 6 );\n\n      TEST_THROWS( tr->set_single_row_mode() );\n      TEST_THROWS( tr->set_chunk_mode( 2 ) );\n\n      tr->send( \"SELECT name, age FROM tao_chunk_mode\" );\n      TEST_THROWS( tr->set_chunk_mode( 0 ) );\n      TEST_THROWS( tr->set_chunk_mode( -1 ) );\n      tr->set_chunk_mode( 2 );\n      tr->set_chunk_mode( 4 );\n      tr->set_single_row_mode();\n      tr->set_chunk_mode( 2 );\n      TEST_THROWS( tr->set_chunk_mode( -1 ) );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n\n#endif\n"
  },
  {
    "path": "test/integration/connection.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <chrono>\n#include <exception>\n#include <iostream>\n#include <string>\n#include <tuple>\n\n#include <tao/pq.hpp>\n#include <tao/pq/internal/unreachable.hpp>\n\nnamespace\n{\n   // LCOV_EXCL_START\n   auto my_poll( const int /*unused*/, const bool /*unused*/, const int /*unused*/ ) -> tao::pq::poll::status\n   {\n      TAO_PQ_INTERNAL_UNREACHABLE;\n   }\n   // LCOV_EXCL_STOP\n\n   void run()\n   {\n      using namespace std::chrono_literals;\n\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );  // NOLINT(clang-analyzer-deadcode.DeadStores)\n\n      // connection_string must be valid\n      TEST_THROWS( tao::pq::connection::create( \"=\" ) );\n\n      // connection_string must reference an existing and accessible database\n      TEST_THROWS( tao::pq::connection::create( \"dbname=DOES_NOT_EXIST\" ) );\n\n      // open a connection\n      const auto connection = tao::pq::connection::create( connection_string );\n      connection->set_timeout( 1s );\n\n      // open a second, independent connection (and discard it immediately)\n      std::ignore = tao::pq::connection::create( connection_string );\n\n      // execute an SQL statement\n      connection->execute( \"DROP TABLE IF EXISTS tao_connection_test\" );\n\n      TEST_THROWS( connection->direct()->get_result() );\n\n      // execution of empty statements fails\n      TEST_THROWS( connection->execute( \"\" ) );\n\n      // execution of invalid statements fails\n      TEST_THROWS( connection->execute( \"FOO BAR BAZ\" ) );\n\n      // a prepared statement's name must be a valid C++ identifier\n      TEST_THROWS( connection->prepare( \"\", \"DROP TABLE IF EXISTS tao_connection_test\" ) );\n      TEST_THROWS( connection->prepare( \"0drop_table\", \"DROP TABLE IF EXISTS tao_connection_test\" ) );\n      TEST_THROWS( connection->prepare( \"drop table\", \"DROP TABLE IF EXISTS tao_connection_test\" ) );\n\n      // prepare a statement\n      connection->prepare( \"drop_table\", \"DROP TABLE IF EXISTS tao_connection_test\" );\n\n      // execute a prepared statement\n      //\n      // note: the name of a prepared statement must be a valid identifier, all\n      // actual SQL statements can be writen in a form which does not match a valid\n      // identifier, so you can always make sure that they can not be confused.\n      connection->execute( \"drop_table\" );\n\n      // statements must consume all parameters\n      TEST_THROWS( connection->execute( \"drop_table\", 42 ) );\n\n      // a statement which is not a query does not return \"affected rows\"\n      TEST_ASSERT( connection->execute( \"drop_table\" ).columns() == 0 );\n\n      // deallocate a prepared statement\n      connection->deallocate( \"drop_table\" );\n\n      // no longer possible\n      TEST_THROWS( connection->execute( \"drop_table\" ) );\n\n      // deallocate must refer to a prepared statement\n      TEST_THROWS( connection->deallocate( \"drop_table\" ) );\n\n      // deallocate must get a valid name\n      TEST_THROWS( connection->deallocate( \"FOO BAR\" ) );\n\n      // test that prepared statement names are case sensitive\n      connection->prepare( \"a\", \"SELECT 1\" );\n      connection->prepare( \"A\", \"SELECT 2\" );\n\n      TEST_THROWS( connection->prepare( \"a\", \"SELECT 2\" ) );\n\n      TEST_ASSERT_MESSAGE( \"checking prepared statement 'a'\", connection->execute( \"a\" ).as< int >() == 1 );\n\n      connection->deallocate( \"a\" );\n\n      TEST_ASSERT_MESSAGE( \"checking prepared statement 'A'\", connection->execute( \"A\" ).as< int >() == 2 );\n\n      connection->prepare( \"a\", \"SELECT 3\" );\n\n      TEST_ASSERT_MESSAGE( \"checking prepared statement 'a'\", connection->execute( \"a\" ).as< int >() == 3 );\n      TEST_ASSERT_MESSAGE( \"checking prepared statement 'A'\", connection->execute( \"A\" ).as< int >() == 2 );\n\n      connection->deallocate( \"A\" );\n\n      TEST_ASSERT_MESSAGE( \"checking prepared statement 'a'\", connection->execute( \"a\" ).as< int >() == 3 );\n\n      connection->prepare( \"A\", \"SELECT 4\" );\n\n      TEST_ASSERT_MESSAGE( \"checking prepared statement 'a'\", connection->execute( \"a\" ).as< int >() == 3 );\n      TEST_ASSERT_MESSAGE( \"checking prepared statement 'A'\", connection->execute( \"A\" ).as< int >() == 4 );\n\n      // create a test table\n      connection->execute( \"CREATE TABLE tao_connection_test ( a INTEGER PRIMARY KEY, b INTEGER )\" );\n\n      // a DELETE statement does not yield a result set\n      TEST_ASSERT( connection->execute( \"DELETE FROM tao_connection_test\" ).columns() == 0 );\n\n      // out of range access throws\n      TEST_THROWS( connection->execute( \"SELECT * FROM tao_connection_test\" ).at( 0 ) );\n\n      // insert some data\n      connection->execute( \"INSERT INTO tao_connection_test VALUES ( 1, 42 )\" );\n\n      TEST_THROWS( connection->execute( \"COPY tao_connection_test ( a, b ) TO STDOUT\" ) );\n      TEST_THROWS( connection->execute( \"COPY tao_connection_test ( a, b ) FROM STDIN\" ) );\n\n      // read data\n      TEST_ASSERT( connection->execute( \"SELECT b FROM tao_connection_test WHERE a = 1\" )[ 0 ][ 0 ].get() == std::string( \"42\" ) );\n\n      TEST_THROWS( connection->execute( \"SELECT $1\" ) );\n      TEST_THROWS( connection->execute( \"SELECT $1\", \"One\", \"Two\" ) );\n\n      TEST_THROWS( connection->execute( \"SELECT $1\", \"\" ).as< tao::pq::binary >() );\n      TEST_THROWS( connection->execute( \"SELECT $1\", \"\\\\\" ).as< tao::pq::binary >() );\n      TEST_THROWS( connection->execute( \"SELECT $1\", \"\\\\xa\" ).as< tao::pq::binary >() );\n      TEST_THROWS( connection->execute( \"SELECT $1\", \"\\\\xa.\" ).as< tao::pq::binary >() );\n\n      {\n         using callback_t = tao::pq::poll::status ( * )( int, bool, int );\n\n         const auto old_cb = *connection->poll_callback().target< callback_t >();\n         TEST_ASSERT( old_cb != nullptr );\n         TEST_ASSERT( *connection->poll_callback().target< callback_t >() != &my_poll );\n         connection->set_poll_callback( my_poll );\n         TEST_ASSERT( *connection->poll_callback().target< callback_t >() == &my_poll );\n         connection->reset_poll_callback();\n         TEST_ASSERT( *connection->poll_callback().target< callback_t >() == old_cb );\n      }\n\n      connection->reset_timeout();\n      TEST_EXECUTE( connection->execute( \"SELECT pg_sleep( 0.2 )\" ) );\n\n      connection->set_timeout( 100ms );\n      TEST_THROWS( connection->execute( \"SELECT pg_sleep( .2 )\" ) );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/connection_pool.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <atomic>\n#include <chrono>\n#include <cstddef>\n#include <exception>\n#include <iostream>\n#include <memory>\n#include <stdexcept>\n\n#include <tao/pq.hpp>\n#include <tao/pq/internal/unreachable.hpp>\n\nnamespace\n{\n   class limited_connection_pool\n      : public tao::pq::connection_pool\n   {\n      struct guard\n      {\n         std::atomic< std::size_t >& m_counter;\n\n         explicit guard( std::atomic< std::size_t >& counter ) noexcept\n            : m_counter( counter )\n         {\n            ++m_counter;\n         }\n\n         guard( const guard& ) = delete;\n         guard( guard&& ) = delete;\n         void operator=( const guard& ) = delete;\n         void operator=( guard&& ) = delete;\n\n         ~guard()\n         {\n            --m_counter;\n         }\n      };\n\n      mutable std::atomic< std::size_t > m_creating = 0;\n\n      using tao::pq::connection_pool::connection_pool;\n\n      [[nodiscard]] auto v_create() const -> std::unique_ptr< tao::pq::connection > override\n      {\n         if( attached() >= 4 || ( m_creating.load() > 2 ) ) {\n            throw std::runtime_error( \"connection limit reached\" );\n         }\n         const guard g( m_creating );\n         return connection_pool::v_create();\n      }\n   };\n\n   // LCOV_EXCL_START\n   auto my_poll( const int /*unused*/, const bool /*unused*/, const int /*unused*/ ) -> tao::pq::poll::status\n   {\n      TAO_PQ_INTERNAL_UNREACHABLE;\n   }\n   // LCOV_EXCL_STOP\n\n   void run()\n   {\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );\n\n      const auto pool = tao::pq::connection_pool::create< limited_connection_pool >( connection_string );\n\n      TEST_ASSERT( pool->empty() );\n      TEST_ASSERT( pool->attached() == 0 );\n\n      TEST_ASSERT( pool->connection() );\n\n      TEST_ASSERT( !pool->empty() );\n      TEST_ASSERT( pool->size() == 1 );\n      TEST_ASSERT( pool->attached() == 0 );\n\n      TEST_ASSERT( pool->connection()->execute( \"SELECT 1\" ).as< int >() == 1 );\n\n      TEST_ASSERT( pool->size() == 1 );\n      TEST_ASSERT( pool->attached() == 0 );\n\n      {\n         const auto conn = pool->connection();\n         TEST_ASSERT( pool->connection() );\n         TEST_ASSERT( conn->execute( \"SELECT 2\" ).as< int >() == 2 );\n\n         TEST_ASSERT( pool->size() == 1 );\n         TEST_ASSERT( pool->attached() == 1 );\n\n         const auto pool2 = tao::pq::connection_pool::create( connection_string );\n         TEST_ASSERT( pool->connection()->execute( \"SELECT 3\" ).as< int >() == 3 );\n         TEST_ASSERT( pool2->connection()->execute( \"SELECT 4\" ).as< int >() == 4 );\n         TEST_ASSERT( conn->execute( \"SELECT 5\" ).as< int >() == 5 );\n         TEST_ASSERT( pool2->connection()->execute( \"SELECT 6\" ).as< int >() == 6 );\n      }\n\n      TEST_ASSERT( pool->size() == 2 );\n      TEST_ASSERT( pool->attached() == 0 );\n      {\n         [[maybe_unused]] const auto c0 = pool->connection();\n         [[maybe_unused]] const auto c1 = pool->connection();\n         TEST_ASSERT( pool->empty() );\n         TEST_ASSERT( pool->attached() == 2 );\n         {\n            [[maybe_unused]] const auto c2 = pool->connection();\n            [[maybe_unused]] const auto c3 = pool->connection();\n            TEST_ASSERT( pool->empty() );\n            TEST_ASSERT( pool->attached() == 4 );\n\n            TEST_THROWS( pool->connection() );\n\n            TEST_ASSERT( pool->empty() );\n            TEST_ASSERT( pool->attached() == 4 );\n         }\n         TEST_ASSERT( pool->size() == 2 );\n         TEST_ASSERT( pool->attached() == 2 );\n      }\n      TEST_ASSERT( pool->size() == 4 );\n      TEST_ASSERT( pool->attached() == 0 );\n\n      {\n         using callback_t = tao::pq::poll::status ( * )( int, bool, int );\n\n         const auto old_cb = *pool->poll_callback().target< callback_t >();\n         TEST_ASSERT( old_cb != nullptr );\n         TEST_ASSERT( *pool->poll_callback().target< callback_t >() != &my_poll );\n         TEST_ASSERT( *pool->connection()->poll_callback().target< callback_t >() != &my_poll );\n         pool->set_poll_callback( my_poll );\n         TEST_ASSERT( *pool->poll_callback().target< callback_t >() == &my_poll );\n         TEST_ASSERT( *pool->connection()->poll_callback().target< callback_t >() == &my_poll );\n         pool->reset_poll_callback();\n         TEST_ASSERT( *pool->poll_callback().target< callback_t >() == old_cb );\n         TEST_ASSERT( *pool->connection()->poll_callback().target< callback_t >() == old_cb );\n      }\n\n      using namespace std::chrono_literals;\n      pool->set_timeout( 100ms );\n      TEST_THROWS( pool->execute( \"SELECT pg_sleep( .5 )\" ) );\n\n      pool->reset_timeout();\n      TEST_EXECUTE( pool->execute( \"SELECT pg_sleep( .5 )\" ) );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/example.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <iostream>\n#include <string>\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   void run()\n   {\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );\n\n      // open a connection to the database\n      const auto conn = tao::pq::connection::create( connection_string );\n\n      // execute statements\n      conn->execute( \"DROP TABLE IF EXISTS tao_example\" );\n      conn->execute( \"CREATE TABLE tao_example ( name TEXT PRIMARY KEY, age INTEGER NOT NULL )\" );\n\n      // prepare statements\n      conn->prepare( \"insert_user\", \"INSERT INTO tao_example ( name, age ) VALUES ( $1, $2 )\" );\n\n      {\n         // begin transaction\n         const auto tr = conn->transaction();\n\n         // execute previously prepared statements\n         tr->execute( \"insert_user\", \"Daniel\", 42 );\n         tr->execute( \"insert_user\", \"Tom\", 41 );\n         tr->execute( \"insert_user\", \"Jerry\", 29 );\n\n         // commit transaction\n         tr->commit();\n      }\n\n      // query data\n      const auto users = conn->execute( \"SELECT name, age FROM tao_example WHERE age >= $1\", 40 );\n\n      // iterate and convert results\n      for( const auto& row : users ) {\n         std::cout << row[ \"name\" ].as< std::string >() << \" is \"\n                   << row[ \"age\" ].as< unsigned >() << \" years old.\\n\";\n      }\n   }\n\n}  // namespace\n\nauto main() -> int\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/exception.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <exception>\n#include <iostream>\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <tao/pq/connection.hpp>\n\nnamespace\n{\n   void run()\n   {\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );\n      const auto connection = tao::pq::connection::create( connection_string );\n      connection->execute( \"DROP TABLE IF EXISTS tao_exception_test\" );\n\n      TEST_THROWS( connection->execute( \"SELECT a FROM tao_exception_test\" ) );\n\n      connection->execute( \"CREATE TABLE tao_exception_test ( a TEXT PRIMARY KEY, b TEXT NOT NULL )\" );\n\n      TEST_THROWS( connection->execute( \"SELECT c FROM tao_exception_test\" ) );\n\n      TEST_THROWS( connection->execute( \"FOO BAR BAZ\" ) );\n      TEST_THROWS( connection->execute( \"SELECT 1/0\" ) );\n      TEST_THROWS( connection->execute( \"SELECT * FROM tao_exception_test WHERE a = 42\" ) );\n      TEST_THROWS( connection->execute( \"SELECT * FROM tao_exception_test WHERE a[0] = 'FOO'\" ) );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/large_object.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/compare.hpp\"\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <ios>\n#include <iostream>\n#include <limits>\n#include <memory>\n#include <string_view>\n#include <utility>\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   template< typename R, typename T >\n   void test( const std::shared_ptr< tao::pq::connection >& connection, const T& data )\n   {\n      const auto transaction = connection->transaction();\n\n      const auto oid = tao::pq::large_object::create( transaction );\n      tao::pq::large_object lo( transaction, oid, std::ios_base::in | std::ios_base::out );\n      lo.write( data );\n\n      lo.seek( 0, std::ios_base::beg );\n\n      const auto result = lo.read< R >( 10 );\n      TEST_ASSERT( tao::pq::internal::compare( result, data ) );\n   }\n\n   void run()\n   {\n      const auto connection = tao::pq::connection::create( tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" ) );\n\n      {\n         const auto transaction = connection->transaction();\n         const auto oid = tao::pq::large_object::create( transaction );\n         tao::pq::large_object lo( transaction, oid, std::ios_base::in | std::ios_base::out );\n         tao::pq::large_object lo2 = std::move( lo );\n         lo = std::move( lo2 );\n         TEST_ASSERT( lo.tell() == 0 );\n         lo.resize( 42 );\n         TEST_ASSERT( lo.seek( 20, std::ios_base::beg ) == 20 );\n         TEST_ASSERT( lo.tell() == 20 );\n         TEST_ASSERT( lo.seek( -20, std::ios_base::end ) == 22 );\n         TEST_ASSERT( lo.tell() == 22 );\n         TEST_ASSERT( lo.seek( 5, std::ios_base::cur ) == 27 );\n         TEST_ASSERT( lo.tell() == 27 );\n         TEST_ASSERT( lo.seek( -7, std::ios_base::cur ) == 20 );\n         TEST_ASSERT( lo.tell() == 20 );\n         TEST_THROWS( lo.seek( -60, std::ios_base::end ) );\n      }\n\n      test< std::string >( connection, std::string( \"hello\" ) );\n      test< std::string >( connection, std::string_view( \"hello\" ) );\n\n      test< tao::pq::binary >( connection, tao::pq::to_binary( \"nice!\" ) );\n      test< tao::pq::binary >( connection, tao::pq::to_binary_view( \"nice!\" ) );\n\n      {\n         const auto transaction = connection->transaction();\n         const auto oid = tao::pq::large_object::create( transaction );\n         tao::pq::large_object lo( transaction, oid, std::ios_base::in );\n         TEST_THROWS( lo.write( \"abc\" ) );\n      }\n\n      {\n         const auto transaction = connection->transaction();\n         const auto oid = tao::pq::large_object::create( transaction );\n         tao::pq::large_object lo( transaction, oid, std::ios_base::out );\n         TEST_THROWS( lo.read( std::numeric_limits< unsigned >::max() ) );\n      }\n\n      {\n         const auto transaction = connection->transaction();\n         const auto oid = tao::pq::large_object::create( transaction );\n         tao::pq::large_object lo( transaction, oid, std::ios_base::in | std::ios_base::out );\n         lo.close();\n      }\n\n      {\n         const auto transaction = connection->transaction();\n         const auto oid = tao::pq::large_object::create( transaction );\n         tao::pq::large_object lo( transaction, oid, std::ios_base::in | std::ios_base::out );\n         TEST_THROWS( lo.resize( -5 ) );\n      }\n\n      {\n         const auto transaction = connection->transaction();\n         const auto oid = tao::pq::large_object::create( transaction );\n         const tao::pq::large_object lo( transaction, oid, std::ios_base::in | std::ios_base::out );\n         tao::pq::large_object::remove( transaction, oid );\n         TEST_THROWS( lo.tell() );\n      }\n\n      {\n         const auto transaction = connection->transaction();\n         const auto oid = tao::pq::large_object::create( transaction );\n         TEST_THROWS( tao::pq::large_object::create( transaction, oid ) );\n      }\n\n      {\n         const auto transaction = connection->transaction();\n         const auto oid = tao::pq::large_object::create( transaction );\n         tao::pq::large_object::remove( transaction, oid );\n         TEST_THROWS( tao::pq::large_object::remove( transaction, oid ) );\n      }\n\n      {\n         const auto transaction = connection->transaction();\n         const auto oid = tao::pq::large_object::create( transaction );\n         tao::pq::large_object::remove( transaction, oid );\n         TEST_THROWS( tao::pq::large_object( transaction, oid, std::ios_base::in | std::ios_base::out ) );\n      }\n\n      {\n         const auto transaction = connection->transaction();\n         const auto oid = tao::pq::large_object::create( transaction );\n         const char* filename = \"dummy.txt\";\n         tao::pq::large_object::export_file( transaction, oid, filename );\n         const auto oid2 = tao::pq::large_object::import_file( transaction, filename );\n         TEST_ASSERT( oid != oid2 );\n         TEST_THROWS( tao::pq::large_object::export_file( transaction, oid, \"\" ) );\n      }\n\n      {\n         const auto transaction = connection->transaction();\n         TEST_THROWS( tao::pq::large_object::import_file( transaction, \"\" ) );\n      }\n   }\n\n}  // namespace\n\nauto main() -> int\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/log.cpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <format>\n#include <iostream>\n#include <string>\n#include <type_traits>\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   [[nodiscard]] auto to_millis( std::chrono::steady_clock::time_point end ) noexcept\n   {\n      return std::chrono::duration_cast< std::chrono::milliseconds >( end - std::chrono::steady_clock::now() ).count();\n   }\n\n   void run()\n   {\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );\n\n      // open a connection to the database\n      const auto conn = tao::pq::connection::create( connection_string );\n\n      // attach a log receiver\n      const auto log = std::make_shared< tao::pq::log >();\n\n      log->connection.send_query = []( tao::pq::connection& c, const char* s, int n, const Oid types[], const char* const values[], const int lengths[], const int formats[] ) {\n         std::ignore = types;\n         std::ignore = lengths;\n         std::ignore = formats;\n         std::cout << std::format( \"send_query(connection={}, statement={}, n_params={})\", static_cast< const void* >( &c ), s, n ) << '\\n';\n         for( int i = 0; i != n; ++i ) {\n            std::cout << std::format( \"  parameter[{}]={}\", i, values[ i ] ) << '\\n';\n         }\n      };\n      log->connection.send_query.result = []( tao::pq::connection& c, int r ) {\n         std::cout << std::format( \"send_query(connection={}) -> {}\", static_cast< const void* >( &c ), r ) << '\\n';\n      };\n      log->connection.send_query_prepared = []( tao::pq::connection& c, const char* s, int n, const char* const values[], const int lengths[], const int formats[] ) {\n         std::ignore = lengths;\n         std::ignore = formats;\n         std::cout << std::format( \"send_query_prepared(connection={}, statement={}, n_params={})\", static_cast< const void* >( &c ), s, n ) << '\\n';\n         for( int i = 0; i != n; ++i ) {\n            std::cout << std::format( \"  parameter[{}]={}\", i, values[ i ] ) << '\\n';\n         }\n      };\n      log->connection.send_query_prepared.result = []( tao::pq::connection& c, int r ) {\n         std::cout << std::format( \"send_query_prepared(connection={}) -> {}\", static_cast< const void* >( &c ), r ) << '\\n';\n      };\n      log->connection.wait = []( tao::pq::connection& c, bool w, std::chrono::steady_clock::time_point e ) {\n         std::cout << std::format( \"wait(connection={}, wait_for_write={}, timeout={} ms)\", static_cast< const void* >( &c ), w, to_millis( e ) ) << '\\n';\n      };\n      log->connection.poll = []( tao::pq::connection& c, int s, bool w, int t ) {\n         std::cout << std::format( \"poll(connection={},socket={}, wait_for_write={}, timeout={} ms)\", static_cast< const void* >( &c ), s, w, t ) << '\\n';\n      };\n      log->connection.poll.result = []( tao::pq::connection& c, int s, tao::pq::poll::status r ) {\n         std::cout << std::format( \"poll(connection={},socket={}) -> {}\", static_cast< const void* >( &c ), s, r ) << '\\n';\n      };\n      log->connection.is_busy.result = []( const tao::pq::connection& c, int r ) {\n         std::cout << std::format( \"is_busy(connection={}) -> {}\", static_cast< const void* >( &c ), r ) << '\\n';\n      };\n      log->connection.consume_input = []( tao::pq::connection& c ) {\n         std::cout << std::format( \"consume_input(connection={})\", static_cast< const void* >( &c ) ) << '\\n';\n      };\n      log->connection.consume_input.result = []( tao::pq::connection& c, int r ) {\n         std::cout << std::format( \"consume_input(connection={}) -> {}\", static_cast< const void* >( &c ), r ) << '\\n';\n      };\n      log->connection.flush = []( tao::pq::connection& c ) {\n         std::cout << std::format( \"flush(connection={})\", static_cast< const void* >( &c ) ) << '\\n';\n      };\n      log->connection.flush.result = []( tao::pq::connection& c, int r ) {\n         std::cout << std::format( \"flush(connection={}) -> {}\", static_cast< const void* >( &c ), r ) << '\\n';\n      };\n      log->connection.get_result = []( tao::pq::connection& c, std::chrono::steady_clock::time_point e ) {\n         std::cout << std::format( \"get_result(connection={}, timeout={} ms)\", static_cast< const void* >( &c ), to_millis( e ) ) << '\\n';\n      };\n      log->connection.get_result.result = []( tao::pq::connection& c, PGresult* r ) {\n         std::cout << std::format( \"get_result(connection={}) -> {}\", static_cast< const void* >( &c ), static_cast< const void* >( r ) ) << '\\n';\n         if( r != nullptr ) {\n            const auto st = PQresultStatus( r );\n            std::cout << std::format( \"  status={}\", PQresStatus( st ) ) << '\\n';\n            const auto cols = PQnfields( r );\n            if( cols != 0 ) {\n               std::cout << std::format( \"  columns={}, rows={}\", cols, PQntuples( r ) ) << '\\n';\n            }\n            else {\n               const char* str = PQcmdTuples( r );\n               if( str[ 0 ] != '\\0' ) {\n                  std::cout << std::format( \"  rows_affected={}\", str ) << '\\n';\n               }\n            }\n         }\n      };\n\n      conn->set_log_handler( log );\n\n      // execute statements\n      conn->execute( \"DROP TABLE IF EXISTS tao_example\" );\n      conn->execute( \"CREATE TABLE tao_example ( name TEXT PRIMARY KEY, age INTEGER NOT NULL )\" );\n\n      // prepare statements\n      conn->prepare( \"insert_user\", \"INSERT INTO tao_example ( name, age ) VALUES ( $1, $2 )\" );\n\n      {\n         // begin transaction\n         const auto tr = conn->transaction();\n\n         // execute previously prepared statements\n         tr->execute( \"insert_user\", \"Daniel\", 42 );\n         tr->execute( \"insert_user\", \"Tom\", 41 );\n         tr->execute( \"insert_user\", \"Jerry\", 29 );\n\n         // commit transaction\n         tr->commit();\n      }\n\n      // query data\n      const auto users = conn->execute( \"SELECT name, age FROM tao_example WHERE age >= $1\", 40 );\n\n      // iterate and convert results\n      for( const auto& row : users ) {\n         std::cout << row[ \"name\" ].as< std::string >() << \" is \"\n                   << row[ \"age\" ].as< unsigned >() << \" years old.\\n\";\n      }\n   }\n\n}  // namespace\n\nauto main() -> int\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/notifications.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <cstddef>\n#include <exception>\n#include <iostream>\n\n#include <tao/pq.hpp>\n\n#if defined( _WIN32 )\n#include <winsock.h>\n#else\n#include <unistd.h>\n#endif\n\nnamespace\n{\n   std::size_t counter = 0;\n\n   void handle_notification( const tao::pq::notification& n )\n   {\n      std::cout << \"channel '\" << n.channel() << \"' received '\" << n.payload() << \"'\\n\";\n      ++counter;\n   }\n\n   std::size_t foo_counter = 0;\n\n   void handle_foo_notification( const char* payload )\n   {\n      std::cout << \"foo handler received '\" << payload << \"'\\n\";\n      ++foo_counter;\n   }\n\n   void run()\n   {\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );\n      const auto connection = tao::pq::connection::create( connection_string );\n\n      TEST_EXECUTE( connection->set_notification_handler( handle_notification ) );\n      TEST_EXECUTE( connection->listen( \"FOO\", handle_foo_notification ) );\n      TEST_ASSERT( counter == 0 );\n      TEST_ASSERT( foo_counter == 0 );\n\n      TEST_EXECUTE( connection->notify( \"FOO\" ) );\n      TEST_ASSERT( counter == 1 );\n      TEST_ASSERT( foo_counter == 1 );\n\n      TEST_ASSERT( connection->notification_handler( \"FOO\" ) );\n      TEST_ASSERT( !connection->notification_handler( \"BAR\" ) );\n      TEST_EXECUTE( connection->reset_notification_handler( \"FOO\" ) );\n      TEST_ASSERT( !connection->notification_handler( \"FOO\" ) );\n\n      TEST_EXECUTE( connection->notify( \"FOO\", \"with payload\" ) );\n      TEST_ASSERT( counter == 2 );\n      TEST_ASSERT( foo_counter == 1 );\n\n      TEST_EXECUTE( connection->unlisten( \"FOO\" ) );\n      TEST_EXECUTE( connection->notify( \"FOO\" ) );\n      TEST_EXECUTE( connection->get_notifications() );\n      TEST_ASSERT( counter == 2 );\n\n      TEST_ASSERT( connection->notification_handler() );\n      TEST_EXECUTE( connection->reset_notification_handler() );\n      TEST_ASSERT( !connection->notification_handler() );\n\n#if defined( _WIN32 )\n      closesocket( connection->socket() );\n#else\n      close( connection->socket() );\n#endif\n\n      TEST_THROWS( connection->get_notifications() );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/parameter.cpp",
    "content": "// Copyright (c) 2023-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <iostream>\n#include <utility>\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   void run()\n   {\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );\n\n      // open a connection to the database\n      const auto conn = tao::pq::connection::create( connection_string );\n\n      // execute statements\n      conn->execute( \"DROP TABLE IF EXISTS tao_parameter_test\" );\n      conn->execute( \"CREATE TABLE tao_parameter_test ( name TEXT PRIMARY KEY, age INTEGER NOT NULL )\" );\n\n      // prepare statements\n      conn->prepare( \"insert_user\", \"INSERT INTO tao_parameter_test ( name, age ) VALUES ( $1, $2 )\" );\n\n      {\n         // begin transaction\n         const auto tr = conn->transaction();\n\n         // execute previously prepared statements\n         {\n            tao::pq::parameter p( \"Daniel\", 42 );\n            tr->execute( \"insert_user\", p );\n            p.reset();\n            p.bind( \"Tom\" );\n            tao::pq::parameter p2( 41 );\n            tr->execute( \"insert_user\", tao::pq::parameter( p, p2 ) );\n         }\n\n         {\n            tao::pq::parameter< 2 > p;\n            std::string s = \"Alice\";\n            p.bind( s );\n            p.bind( std::move( s ) );\n            p.reset( std::string( \"Jerry\" ), 42 + 7 );\n            p.bind();\n            tao::pq::parameter<> p2( p );\n            tr->execute( \"insert_user\", p2 );\n         }\n\n         {\n            tao::pq::parameter< 1 > p;\n            std::string s = \"Alice\";\n            p.bind( std::move( s ) );\n            tr->execute( \"insert_user\", p, 44 );\n         }\n\n         // commit transaction\n         tr->commit();\n      }\n\n      // query data\n      const auto users = conn->execute( \"SELECT name, age FROM tao_parameter_test WHERE age >= $1\", 40 );\n\n      // iterate and convert results\n      for( const auto& row : users ) {\n         std::cout << row[ \"name\" ].as< std::string >() << \" is \"\n                   << row[ \"age\" ].as< unsigned >() << \" years old.\\n\";\n      }\n\n      {\n         tao::pq::parameter< 1 > p;\n         p.bind( 1 );\n         TEST_THROWS( p.bind( 2 ) );\n      }\n\n      {\n         tao::pq::parameter< 1 > p;\n         tao::pq::parameter< 1 > p2;\n         p.bind( 1 );\n         p2.bind( 1 );\n         TEST_THROWS( p.bind( p2 ) );\n      }\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/password.cpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <format>\n#include <iostream>\n#include <string>\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   void run()\n   {\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );\n\n      // open a connection to the database\n      const auto conn = tao::pq::connection::create( connection_string );\n\n      // prevent cleartext passwords from showing up in logs, traces, etc.\n      const std::string cleartext_password = \"secret123\";\n      const auto encrypted_password = conn->password( cleartext_password, \"tao_test_role\" );\n\n      // execute commands\n      conn->execute( \"DROP ROLE IF EXISTS tao_test_role\" );\n      conn->execute( std::format( \"CREATE ROLE tao_test_role PASSWORD '{}'\", encrypted_password ) );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/pipeline_mode.cpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <iostream>\n\n#include <tao/pq.hpp>\n#include <tao/pq/internal/unreachable.hpp>\n\nnamespace\n{\n   void run()\n   {\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );  // NOLINT(clang-analyzer-deadcode.DeadStores)\n\n      // open a connection\n      const auto connection = tao::pq::connection::create( connection_string );\n      TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::off );\n\n      TEST_THROWS( connection->pipeline_sync() );\n\n      connection->exit_pipeline_mode();\n      TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::off );\n\n      connection->enter_pipeline_mode();\n      TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::on );\n\n      connection->exit_pipeline_mode();\n      TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::off );\n\n      {\n         auto tr = connection->direct();\n         TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::off );\n\n         connection->enter_pipeline_mode();\n         TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::on );\n\n         tr->send( \"SELECT 42\" );\n         tr->send( \"SELECT 1234\" );\n         connection->pipeline_sync();\n\n         TEST_ASSERT( tr->get_result().as< int >() == 42 );\n\n         tr->send( \"SELECT 1701\" );\n         connection->pipeline_sync();\n\n         TEST_ASSERT( tr->get_result().as< int >() == 1234 );\n         tr->consume_pipeline_sync();\n\n         TEST_ASSERT( tr->get_result().as< int >() == 1701 );\n         tr->consume_pipeline_sync();\n\n         TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::on );\n         connection->exit_pipeline_mode();\n         TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::off );\n\n         tr->commit();\n      }\n\n      {\n         auto tr = connection->transaction();\n         TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::off );\n\n         connection->enter_pipeline_mode();\n         TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::on );\n\n         tr->send( \"SELECT 42\" );\n         tr->send( \"SELECT 1234\" );\n         connection->pipeline_sync();\n\n         TEST_ASSERT( tr->get_result().as< int >() == 42 );\n\n         tr->send( \"SELECT 1701\" );\n         connection->pipeline_sync();\n\n         TEST_ASSERT( tr->get_result().as< int >() == 1234 );\n         tr->consume_pipeline_sync();\n\n         TEST_ASSERT( tr->get_result().as< int >() == 1701 );\n         tr->consume_pipeline_sync();\n\n         TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::on );\n         connection->exit_pipeline_mode();\n         TEST_ASSERT( connection->pipeline_status() == tao::pq::pipeline_status::off );\n\n         tr->commit();\n      }\n\n      {\n         auto pl = connection->pipeline();\n\n         pl->send( \"SELECT 42\" );\n         pl->send( \"SELECT 1234\" );\n         pl->sync();\n\n         TEST_ASSERT( pl->get_result().as< int >() == 42 );\n\n         pl->send( \"SELECT 1701\" );\n         pl->sync();\n\n         TEST_ASSERT( pl->get_result().as< int >() == 1234 );\n         pl->consume_sync();\n\n         TEST_ASSERT( pl->get_result().as< int >() == 1701 );\n         pl->consume_sync();\n\n         pl->finish();\n      }\n\n      {\n         auto pl = connection->transaction()->pipeline();\n\n         pl->send( \"SELECT 42\" );\n         pl->send( \"SELECT 1234\" );\n         pl->sync();\n\n         TEST_ASSERT( pl->get_result().as< int >() == 42 );\n\n         pl->send( \"SELECT 1701\" );\n         pl->sync();\n\n         TEST_ASSERT( pl->get_result().as< int >() == 1234 );\n         pl->consume_sync();\n\n         TEST_ASSERT( pl->get_result().as< int >() == 1701 );\n         pl->consume_sync();\n\n         pl->finish();\n      }\n\n      {\n         connection->execute( \"DROP TABLE IF EXISTS tao_pipeline_mode\" );\n         connection->execute( \"CREATE TABLE tao_pipeline_mode ( name TEXT PRIMARY KEY, age INTEGER NOT NULL )\" );\n\n         connection->prepare( \"insert_user\", \"INSERT INTO tao_pipeline_mode ( name, age ) VALUES ( $1, $2 )\" );\n\n         auto pl = connection->pipeline();\n\n         pl->send( \"insert_user\", \"Daniel\", 42 );\n         pl->send( \"insert_user\", \"Tom\", 41 );\n         pl->send( \"insert_user\", \"Jerry\", 29 );\n         pl->sync();\n\n         TEST_ASSERT( pl->get_result().rows_affected() == 1 );  // daniel\n         TEST_ASSERT( pl->get_result().rows_affected() == 1 );  // tom\n         TEST_ASSERT( pl->get_result().rows_affected() == 1 );  // jerry\n         TEST_EXECUTE( pl->consume_sync() );\n\n         pl->send( \"SELECT name, age FROM tao_pipeline_mode\" );\n         pl->send( \"SELECT name, age FROM tao_pipeline_mode\" );\n         pl->sync();\n\n         pl->set_single_row_mode();\n\n         TEST_ASSERT( pl->get_result().size() == 1 );\n         TEST_ASSERT( pl->get_result().size() == 1 );\n         TEST_ASSERT( pl->get_result().size() == 1 );\n         TEST_ASSERT( pl->get_result().empty() );\n\n         TEST_ASSERT( pl->get_result().size() == 3 );\n\n         TEST_EXECUTE( pl->consume_sync() );\n\n         pl->finish();\n      }\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/result.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <iostream>\n#include <optional>\n#include <ranges>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   void run()  // NOLINT(readability-function-size)\n   {\n      static_assert( std::ranges::range< tao::pq::result > );\n      static_assert( std::ranges::bidirectional_range< tao::pq::result > );\n      static_assert( std::ranges::sized_range< tao::pq::result > );\n\n      // Even though we are *almost* a random access range,\n      // we can't fully satisfy the requirements.\n      // static_assert( std::ranges::random_access_range< tao::pq::result > );\n\n      const auto connection = tao::pq::connection::create( tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" ) );\n\n      TEST_EXECUTE( connection->execute( \"SELECT NULL\" ) );\n      TEST_ASSERT( connection->execute( \"SELECT NULL\" ).is_null( 0, 0 ) );\n      TEST_THROWS( connection->execute( \"SELECT NULL\" ).is_null( 0, 1 ) );\n      TEST_THROWS( connection->execute( \"SELECT NULL\" ).is_null( 1, 0 ) );\n      TEST_THROWS( connection->execute( \"SELECT NULL\" ).get( 0, 0 ) );\n      TEST_THROWS( connection->execute( \"SELECT NULL\" ).get( 0, 1 ) );\n      TEST_THROWS( connection->execute( \"SELECT NULL\" ).get( 1, 0 ) );\n      TEST_ASSERT( connection->execute( \"SELECT NULL\" )[ 0 ].is_null( 0 ) );\n      TEST_THROWS( connection->execute( \"SELECT NULL\" )[ 0 ].is_null( 1 ) );\n      TEST_ASSERT( connection->execute( \"SELECT NULL\" )[ 0 ][ 0 ].is_null() );\n      TEST_ASSERT( connection->execute( \"SELECT NULL\" )[ 0 ][ 0 ] == tao::pq::null );\n      TEST_ASSERT( tao::pq::null == connection->execute( \"SELECT NULL\" )[ 0 ][ 0 ] );\n      TEST_ASSERT( !( connection->execute( \"SELECT NULL\" )[ 0 ][ 0 ] != tao::pq::null ) );\n      TEST_ASSERT( !( tao::pq::null != connection->execute( \"SELECT NULL\" )[ 0 ][ 0 ] ) );\n      TEST_THROWS( connection->execute( \"SELECT NULL\" )[ 0 ].at( 1 ) );\n      TEST_THROWS( connection->execute( \"SELECT NULL\" ).at( 1 )[ 1 ] );\n\n      TEST_EXECUTE( connection->execute( \"SELECT 42\" ) );\n      TEST_ASSERT( !connection->execute( \"SELECT 42\" ).is_null( 0, 0 ) );\n      TEST_THROWS( connection->execute( \"SELECT 42\" ).is_null( 0, 1 ) );\n      TEST_THROWS( connection->execute( \"SELECT 42\" ).is_null( 1, 0 ) );\n      TEST_ASSERT( connection->execute( \"SELECT 42\" ).get( 0, 0 ) == std::string( \"42\" ) );\n      TEST_THROWS( connection->execute( \"SELECT 42\" ).get( 0, 1 ) );\n      TEST_THROWS( connection->execute( \"SELECT 42\" ).get( 1, 0 ) );\n      TEST_ASSERT( !connection->execute( \"SELECT 42\" )[ 0 ].is_null( 0 ) );\n      TEST_THROWS( connection->execute( \"SELECT 42\" )[ 0 ].is_null( 1 ) );\n      TEST_ASSERT( !connection->execute( \"SELECT 42\" )[ 0 ][ 0 ].is_null() );\n      TEST_ASSERT( connection->execute( \"SELECT 42\" )[ 0 ][ 0 ] != tao::pq::null );\n      TEST_ASSERT( tao::pq::null != connection->execute( \"SELECT 42\" )[ 0 ][ 0 ] );\n      TEST_ASSERT( !( connection->execute( \"SELECT 42\" )[ 0 ][ 0 ] == tao::pq::null ) );\n      TEST_ASSERT( !( tao::pq::null == connection->execute( \"SELECT 42\" )[ 0 ][ 0 ] ) );\n      TEST_THROWS( connection->execute( \"SELECT 42\" )[ 0 ].at( 1 ) );\n      TEST_THROWS( connection->execute( \"SELECT 42\" ).at( 1 )[ 1 ] );\n\n      TEST_ASSERT( connection->execute( \"SELECT 42\" ).as< int >() == 42 );\n      TEST_ASSERT( connection->execute( \"SELECT 1764\" ).optional< int >() == 1764 );\n      TEST_ASSERT( !connection->execute( \"SELECT 64 WHERE FALSE\" ).optional< int >() );\n      TEST_ASSERT( !connection->execute( \"SELECT NULL\" ).as< std::optional< int > >() );\n\n      TEST_ASSERT( connection->execute( \"SELECT 42\" ).tuple< int >() == std::tuple< int >( 42 ) );\n\n      TEST_ASSERT( connection->execute( \"SELECT 1, 2\" ).pair< int, int >() == std::pair< int, int >( 1, 2 ) );\n      TEST_ASSERT( connection->execute( \"SELECT 1, 2, 3, 4\" ).tuple< int, int, int, int >() == std::tuple< int, int, int, int >( 1, 2, 3, 4 ) );\n\n      TEST_ASSERT( connection->execute( \"SELECT 42\" ).columns() == 1 );\n      TEST_ASSERT( connection->execute( \"SELECT 42\" ).vector< int >().size() == 1 );\n      TEST_ASSERT( connection->execute( \"SELECT 42\" ).list< int >().size() == 1 );\n      TEST_ASSERT( connection->execute( \"SELECT 42\" ).set< int >().size() == 1 );\n      TEST_ASSERT( connection->execute( \"SELECT 42\" ).multiset< int >().size() == 1 );\n      TEST_ASSERT( connection->execute( \"SELECT 42\" ).unordered_set< int >().size() == 1 );\n      TEST_ASSERT( connection->execute( \"SELECT 42\" ).unordered_multiset< int >().size() == 1 );\n\n      TEST_THROWS( connection->execute( \"SELECT 42\" ).as< bool >() );\n\n      TEST_ASSERT( connection->execute( \"SELECT 1, 2\" ).columns() == 2 );\n      TEST_ASSERT( connection->execute( \"SELECT 1, 2\" ).map< int, int >().size() == 1 );\n      TEST_ASSERT( connection->execute( \"SELECT 1, 2\" ).multimap< int, int >().size() == 1 );\n      TEST_ASSERT( connection->execute( \"SELECT 1, 2\" ).unordered_map< int, int >().size() == 1 );\n      TEST_ASSERT( connection->execute( \"SELECT 1, 2\" ).unordered_multimap< int, int >().size() == 1 );\n\n      TEST_ASSERT( connection->execute( \"SELECT 1 UNION ALL SELECT 2\" ).list< int >().size() == 2 );\n      TEST_ASSERT( connection->execute( \"SELECT 1, 2, 3, 4 UNION ALL SELECT 5, 6, 7, 8\" ).list< std::tuple< int, int, int, int > >().size() == 2 );\n      TEST_ASSERT( connection->execute( \"SELECT 1, 2 UNION ALL SELECT 2, 5 UNION ALL SELECT 3, 42\" ).map< int, int >().size() == 3 );\n\n      const auto result = connection->execute( \"SELECT 1 AS a, 2 AS B, 3 AS \\\"C\\\"\" );\n\n      TEST_ASSERT( result.has_rows_affected() );\n      TEST_ASSERT( !result.empty() );\n      TEST_ASSERT( result.size() == 1 );\n      TEST_ASSERT( result.columns() == 3 );\n      TEST_ASSERT( result.at( 0 ).columns() == 3 );\n      TEST_THROWS( result.at( 1 ) );\n\n      TEST_ASSERT( result.name( 0 ) == \"a\" );\n      TEST_ASSERT( result.name( 1 ) == \"b\" );\n      TEST_ASSERT( result.name( 2 ) == \"C\" );\n\n      TEST_ASSERT( result.index( \"a\" ) == 0 );\n      TEST_ASSERT( result.index( \"A\" ) == 0 );\n      TEST_ASSERT( result.index( \"\\\"a\\\"\" ) == 0 );\n      TEST_THROWS( result.index( \"\\\"A\\\"\" ) );\n\n      TEST_ASSERT( result.index( \"b\" ) == 1 );\n      TEST_ASSERT( result.index( \"B\" ) == 1 );\n      TEST_ASSERT( result.index( \"\\\"b\\\"\" ) == 1 );\n      TEST_THROWS( result.index( \"\\\"B\\\"\" ) );\n\n      TEST_THROWS( result.index( \"c\" ) );\n      TEST_THROWS( result.index( \"C\" ) );\n      TEST_THROWS( result.index( \"\\\"c\\\"\" ) );\n      TEST_ASSERT( result.index( \"\\\"C\\\"\" ) == 2 );\n\n      TEST_THROWS( connection->execute( \"SELECT 42 WHERE FALSE\" ).as< int >() );\n      TEST_THROWS( connection->execute( \"SELECT 1 UNION ALL SELECT 2\" ).as< int >() );\n\n      TEST_THROWS( connection->execute( \"SELECT 42\" ).pair< int, int >() );\n      TEST_THROWS( connection->execute( \"SELECT 1, 2\" ).as< int >() );\n\n      TEST_THROWS( connection->execute( \"SELECT ''\" ).as< char >() );\n      TEST_THROWS( connection->execute( \"SELECT 'Hallo'\" ).as< char >() );\n\n      TEST_THROWS( connection->execute( \"SELECT ''\" ).as< signed char >() );\n      TEST_THROWS( connection->execute( \"SELECT 'Hallo'\" ).as< signed char >() );\n      TEST_THROWS( connection->execute( \"SELECT -129\" ).as< signed char >() );\n      TEST_ASSERT( connection->execute( \"SELECT -128\" ).as< signed char >() == -128 );\n      TEST_ASSERT( connection->execute( \"SELECT 127\" ).as< signed char >() == 127 );\n      TEST_THROWS( connection->execute( \"SELECT 128\" ).as< signed char >() );\n\n      TEST_THROWS( connection->execute( \"SELECT ''\" ).as< unsigned char >() );\n      TEST_THROWS( connection->execute( \"SELECT 'Hallo'\" ).as< unsigned char >() );\n      TEST_ASSERT( connection->execute( \"SELECT 255\" ).as< unsigned char >() == 255 );\n      TEST_THROWS( connection->execute( \"SELECT 256\" ).as< unsigned char >() );\n\n      TEST_THROWS( connection->execute( \"SELECT ''\" ).as< short >() );\n      TEST_THROWS( connection->execute( \"SELECT 'Hallo'\" ).as< short >() );\n      TEST_THROWS( connection->execute( \"SELECT -32769\" ).as< short >() );\n      TEST_ASSERT( connection->execute( \"SELECT -32768\" ).as< short >() == -32768 );\n      TEST_ASSERT( connection->execute( \"SELECT 32767\" ).as< short >() == 32767 );\n      TEST_THROWS( connection->execute( \"SELECT 32768\" ).as< short >() );\n\n      TEST_THROWS( connection->execute( \"SELECT ''\" ).as< unsigned short >() );\n      TEST_THROWS( connection->execute( \"SELECT 'Hallo'\" ).as< unsigned short >() );\n      TEST_ASSERT( connection->execute( \"SELECT 65535\" ).as< unsigned short >() == 65535 );\n      TEST_THROWS( connection->execute( \"SELECT 65536\" ).as< unsigned short >() );\n\n      TEST_THROWS( connection->execute( \"SELECT ''\" ).as< int >() );\n      TEST_THROWS( connection->execute( \"SELECT 'Hallo'\" ).as< int >() );\n      TEST_THROWS( connection->execute( \"SELECT -2147483649\" ).as< int >() );\n      TEST_ASSERT( connection->execute( \"SELECT -2147483648\" ).as< int >() == -2147483648LL );\n      TEST_ASSERT( connection->execute( \"SELECT 2147483647\" ).as< int >() == 2147483647 );\n      TEST_THROWS( connection->execute( \"SELECT 2147483648\" ).as< int >() );\n\n      TEST_THROWS( connection->execute( \"SELECT ''\" ).as< unsigned >() );\n      TEST_THROWS( connection->execute( \"SELECT 'Hallo'\" ).as< unsigned >() );\n      TEST_ASSERT( connection->execute( \"SELECT 4294967295\" ).as< unsigned >() == 4294967295 );\n      TEST_THROWS( connection->execute( \"SELECT 4294967296\" ).as< unsigned >() );\n\n      TEST_THROWS( connection->execute( \"SELECT '42 FOO'\" ).as< unsigned >() );\n      TEST_THROWS( connection->execute( \"SELECT '42BAR'\" ).as< unsigned >() );\n\n      int count = 0;\n      for( const auto& row : connection->execute( \"SELECT 1 UNION ALL SELECT 2\" ) ) {\n         for( const auto& field : row ) {\n            TEST_ASSERT( field.as< int >() == ++count );\n         }\n      }\n      TEST_ASSERT( count == 2 );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/row.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <iostream>\n#include <optional>\n#include <ranges>\n#include <tuple>\n#include <utility>\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   void run()\n   {\n      static_assert( std::ranges::range< tao::pq::row > );\n      static_assert( std::ranges::bidirectional_range< tao::pq::row > );\n      static_assert( std::ranges::sized_range< tao::pq::row > );\n\n      // Even though we are *almost* a random access range,\n      // we can't fully satisfy the requirements.\n      // static_assert( std::ranges::random_access_range< tao::pq::row > );\n\n      const auto connection = tao::pq::connection::create( tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" ) );\n\n      TEST_ASSERT( connection->execute( \"SELECT 42\" )[ 0 ].as< int >() == 42 );\n      TEST_ASSERT( connection->execute( \"SELECT 1764\" ).at( 0 ).optional< int >() == 1764 );\n      TEST_ASSERT( !connection->execute( \"SELECT NULL\" )[ 0 ].as< std::optional< int > >() );\n\n      TEST_ASSERT( connection->execute( \"SELECT 42\" )[ 0 ][ 0 ].get() == std::string( \"42\" ) );\n      TEST_ASSERT( connection->execute( \"SELECT 42\" )[ 0 ][ 0 ].as< int >() == 42 );\n      TEST_ASSERT( connection->execute( \"SELECT 42\" )[ 0 ][ 0 ].optional< int >() == 42 );\n\n      TEST_ASSERT( connection->execute( \"SELECT 1, 2\" )[ 0 ].pair< int, int >() == std::pair< int, int >( 1, 2 ) );\n      TEST_ASSERT( connection->execute( \"SELECT 1, 2, 3, 4\" )[ 0 ].tuple< int, int, int, int >() == std::tuple< int, int, int, int >( 1, 2, 3, 4 ) );\n\n      TEST_THROWS( connection->execute( \"SELECT 42\" )[ 0 ].as< bool >() );\n\n      const auto result = connection->execute( \"SELECT 1 AS a, 2 AS B, 3 AS \\\"C\\\", 4 as \\\"A\\\"\" );\n      const auto& row = result[ 0 ];\n\n      TEST_ASSERT( row.columns() == 4 );\n\n      TEST_ASSERT( row.name( 0 ) == \"a\" );\n      TEST_ASSERT( row.name( 1 ) == \"b\" );\n      TEST_ASSERT( row.name( 2 ) == \"C\" );\n      TEST_ASSERT( row.name( 3 ) == \"A\" );\n      TEST_THROWS( row.name( 4 ) );\n\n      TEST_ASSERT( row.at( 0 ).name() == \"a\" );\n      TEST_ASSERT( row[ 1 ].name() == \"b\" );\n      TEST_ASSERT( row.at( 2 ).name() == \"C\" );\n      TEST_ASSERT( row[ 3 ].name() == \"A\" );\n      TEST_THROWS( row.at( 4 ) );\n\n      TEST_ASSERT( row.index( \"a\" ) == 0 );\n      TEST_ASSERT( row.index( \"A\" ) == 0 );\n      TEST_ASSERT( row.index( \"\\\"a\\\"\" ) == 0 );\n      TEST_ASSERT( row.index( \"\\\"A\\\"\" ) == 3 );\n\n      TEST_ASSERT( row.index( \"b\" ) == 1 );\n      TEST_ASSERT( row.index( \"B\" ) == 1 );\n      TEST_ASSERT( row.index( \"\\\"b\\\"\" ) == 1 );\n      TEST_THROWS( row.index( \"\\\"B\\\"\" ) );\n\n      TEST_THROWS( row.index( \"c\" ) );\n      TEST_THROWS( row.index( \"C\" ) );\n      TEST_THROWS( row.index( \"\\\"c\\\"\" ) );\n      TEST_ASSERT( row.index( \"\\\"C\\\"\" ) == 2 );\n\n      TEST_THROWS( row.get< std::string >( 4 ) );\n      TEST_THROWS( row.get< std::optional< std::string > >( 4 ) );\n      TEST_THROWS( row.get< std::pair< std::string, std::string > >( 3 ) );\n\n      const auto result2 = connection->execute( \"SELECT 1 AS a, 2 AS b, 3 AS a\" );\n      const auto& row2 = result2[ 0 ];\n\n      TEST_ASSERT( row2.index( \"a\" ) == 0 );\n      TEST_ASSERT( row2.index( \"A\" ) == 0 );\n      TEST_ASSERT( row2.slice( 1, 2 ).index( \"a\" ) == 1 );\n      TEST_ASSERT( row2.slice( 1, 2 ).index( \"A\" ) == 1 );\n\n      TEST_THROWS( row2.slice( 1, 1 ).index( \"a\" ) );\n      TEST_THROWS( row2.slice( 1, 1 ).index( \"A\" ) );\n      TEST_THROWS( row2.slice( 2, 1 ).index( \"b\" ) );\n      TEST_THROWS( row2.slice( 2, 1 ).index( \"B\" ) );\n\n      TEST_THROWS( row2.slice( 0, 0 ) );\n      TEST_THROWS( row2.slice( 1, 0 ) );\n      TEST_THROWS( row2.slice( 2, 0 ) );\n\n      TEST_THROWS( row2.slice( 0, 4 ) );\n      TEST_THROWS( row2.slice( 1, 3 ) );\n      TEST_THROWS( row2.slice( 2, 2 ) );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/single_row_mode.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <iostream>\n#include <string>\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   void run()\n   {\n      // overwrite the default with an environment variable if needed\n      const auto connection_string = tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" );\n\n      // open a connection to the database\n      const auto conn = tao::pq::connection::create( connection_string );\n\n      conn->execute( \"DROP TABLE IF EXISTS tao_single_row_mode\" );\n      conn->execute( \"CREATE TABLE tao_single_row_mode ( name TEXT PRIMARY KEY, age INTEGER NOT NULL )\" );\n\n      conn->prepare( \"insert_user\", \"INSERT INTO tao_single_row_mode ( name, age ) VALUES ( $1, $2 )\" );\n      conn->execute( \"insert_user\", \"Daniel\", 42 );\n      conn->execute( \"insert_user\", \"Tom\", 41 );\n      conn->execute( \"insert_user\", \"Jerry\", 29 );\n\n      const auto tr = conn->transaction();\n      tr->send( \"SELECT name, age FROM tao_single_row_mode\" );\n      tr->set_single_row_mode();\n\n      while( true ) {\n         // in single row mode, each result contains either\n         // a single row or no row when the end is reached.\n         const auto result = tr->get_result();\n         if( result.empty() ) {\n            break;\n         }\n\n         // the loop is unnecessary for single row mode,\n         // but in chunked mode multiple rows per result are possible.\n         for( const auto& row : result ) {\n            std::cout << row[ \"name\" ].as< std::string >() << \" is \"\n                      << row[ \"age\" ].as< unsigned >() << \" years old.\\n\";\n         }\n      }\n\n      TEST_THROWS( tr->set_single_row_mode() );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/table_reader.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/compare.hpp\"\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <cstddef>\n#include <exception>\n#include <iostream>\n#include <optional>\n#include <string_view>\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   void run()\n   {\n      const auto connection = tao::pq::connection::create( tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" ) );\n      connection->execute( \"DROP TABLE IF EXISTS tao_table_reader_test\" );\n      connection->execute( \"CREATE TABLE tao_table_reader_test ( a INTEGER NOT NULL, b DOUBLE PRECISION, c TEXT )\" );\n\n      // we use a table_writer to fill the table with 100.000 rows.\n      {\n         tao::pq::table_writer tw( connection->direct(), \"COPY tao_table_reader_test ( a, b, c ) FROM STDIN\" );\n         for( unsigned n = 0; n < 100000; ++n ) {\n            tw.insert( n, n / 100.0, \"EUR\" );\n         }\n         TEST_ASSERT_MESSAGE( \"validate reported result size\", tw.commit() == 100000 );\n         TEST_ASSERT_MESSAGE( \"validate actual result size\", connection->execute( \"SELECT COUNT(*) FROM tao_table_reader_test\" ).as< std::size_t >() == 100000 );\n      }\n\n      {\n         tao::pq::table_reader tr( connection->direct(), \"COPY tao_table_reader_test ( a, b, c ) TO STDOUT\" );\n         TEST_THROWS( connection->direct() );\n\n         std::size_t count = 0;\n         for( const auto& row : tr ) {\n            for( const auto& field : row ) {\n               if( !field.is_null() ) {\n                  ++count;\n               }\n            }\n         }\n         TEST_ASSERT_MESSAGE( \"validate count\", count == 300000 );\n      }\n\n      TEST_THROWS( tao::pq::table_reader( connection->direct(), \"SELECT 42\" ) );\n      TEST_THROWS( tao::pq::table_reader( connection->direct(), \"\" ) );\n      TEST_THROWS( tao::pq::table_reader( connection->direct(), \"COPY tao_table_reader_test ( a, b, c, d ) TO STDOUT\" ) );\n      TEST_THROWS( tao::pq::table_reader( connection->direct(), \"COPY tao_table_reader_test ( a, b, c ) FROM STDIN\" ) );\n\n      TEST_THROWS( connection->execute( \"COPY tao_table_reader_test ( a, b, c ) TO STDOUT\" ) );\n\n      connection->execute( \"DROP TABLE IF EXISTS tao_table_reader_test\" );\n      connection->execute( \"CREATE TABLE tao_table_reader_test ( a BYTEA )\" );\n      {\n         tao::pq::table_writer tw( connection->direct(), \"COPY tao_table_reader_test ( a ) FROM STDIN\" );\n         tw.insert( tao::pq::to_binary_view( \"1\" ) );\n         tw.insert( tao::pq::binary_view() );\n         tw.insert( tao::pq::null );\n         tw.insert( tao::pq::to_binary_view( \"F\\\"O\\\\O\" ) );\n         tw.insert( tao::pq::to_binary_view( \"NU\\0LL\" ) );\n         TEST_ASSERT( tw.commit() == 5 );\n\n         tao::pq::table_reader tr( connection->direct(), \"COPY tao_table_reader_test ( a ) TO STDOUT\" );\n         const auto result = tr.vector< std::optional< tao::pq::binary > >();\n         TEST_ASSERT( result.size() == 5 );\n         TEST_ASSERT( tao::pq::internal::compare( result[ 0 ].value(), tao::pq::to_binary_view( \"1\" ) ) );\n         TEST_ASSERT( tao::pq::internal::compare( result[ 1 ].value(), tao::pq::binary_view() ) );\n         TEST_ASSERT( !result[ 2 ] );\n         TEST_ASSERT( tao::pq::internal::compare( result[ 3 ].value(), tao::pq::to_binary_view( \"F\\\"O\\\\O\" ) ) );\n         TEST_ASSERT( tao::pq::internal::compare( result[ 4 ].value(), tao::pq::to_binary_view( \"NU\\0LL\" ) ) );\n      }\n\n      connection->execute( \"DROP TABLE IF EXISTS tao_table_reader_test\" );\n      connection->execute( \"CREATE TABLE tao_table_reader_test ( a INTEGER NOT NULL, b DOUBLE PRECISION, c TEXT )\" );\n      connection->execute( \"INSERT INTO tao_table_reader_test VALUES( $1, $2, $3 )\", 1, 1.234567, \"A\\bB\\fC\\\"D'E\\n\\rF\\tGH\\vI\\\\J\" );\n      connection->execute( \"INSERT INTO tao_table_reader_test VALUES( $1, $2, $3 )\", 2, tao::pq::null, tao::pq::null );\n      connection->execute( \"INSERT INTO tao_table_reader_test VALUES( $1, $2, $3 )\", 3, 42, \"FOO\" );\n\n      {\n         tao::pq::table_reader tr( connection->direct(), \"COPY tao_table_reader_test ( a, b, c ) TO STDOUT\" );\n         TEST_ASSERT( tr.columns() == 3 );\n         {\n            TEST_ASSERT( tr.get_row() );\n            const auto& row = tr.row();\n            auto [ a, b, c ] = row.tuple< int, std::optional< double >, std::optional< std::string_view > >();\n            TEST_ASSERT( a == 1 );\n            TEST_ASSERT( b == 1.234567 );\n            TEST_ASSERT( c == \"A\\bB\\fC\\\"D'E\\n\\rF\\tGH\\vI\\\\J\" );\n\n            TEST_ASSERT( row.at( 0 ).as< int >() == 1 );\n            TEST_ASSERT( !row[ 1 ].is_null() );\n            TEST_ASSERT( row[ 1 ].get() == std::string_view( \"1.234567\" ) );\n            TEST_ASSERT( row[ 1 ].optional< double >() == 1.234567 );\n            TEST_THROWS( row.at( 3 ) );\n            TEST_THROWS( row.slice( 0, 0 ) );\n            TEST_THROWS( row.slice( 1, 0 ) );\n            TEST_THROWS( row.slice( 0, 4 ) );\n            TEST_THROWS( row.tuple< int, std::optional< double > >() );\n            TEST_THROWS( row.tuple< int, std::optional< double >, std::optional< std::string_view >, std::optional< std::string_view > >() );\n         }\n         {\n            TEST_ASSERT( tr.get_row() );\n            auto [ a, b, c ] = tr.row().tuple< int, std::optional< double >, std::optional< std::string_view > >();\n            TEST_ASSERT( a == 2 );\n            TEST_ASSERT( !b );\n            TEST_ASSERT( !c );\n            TEST_THROWS( tr.row().tuple< int, double, std::string_view >() );\n         }\n         PQclear( PQexec( connection->underlying_raw_ptr(), \"SELECT 42\" ) );\n         TEST_THROWS( tr.get_row() );\n      }\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/table_writer.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <cstddef>\n#include <exception>\n#include <iostream>\n#include <optional>\n#include <tuple>\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   void run()\n   {\n      const auto connection = tao::pq::connection::create( tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" ) );\n      connection->execute( \"DROP TABLE IF EXISTS tao_table_writer_test\" );\n      connection->execute( \"CREATE TABLE tao_table_writer_test ( a INTEGER NOT NULL, b DOUBLE PRECISION, c TEXT )\" );\n\n      tao::pq::table_writer tw( connection->direct(), \"COPY tao_table_writer_test ( a, b, c ) FROM STDIN\" );\n      TEST_THROWS( connection->direct() );\n      for( unsigned n = 0; n < 100000; ++n ) {\n         tw.insert( n, n + 23.45, \"EUR\" );\n      }\n\n      tw.insert( std::make_tuple( 123456, tao::pq::null, \"EUR\\nUSD\\\"FOO\\\\BAR\" ) );\n\n      TEST_ASSERT_MESSAGE( \"validate reported result size\", tw.commit() == 100001 );\n      TEST_ASSERT_MESSAGE( \"validate actual result size\", connection->execute( \"SELECT COUNT(*) FROM tao_table_writer_test\" ).as< std::size_t >() == 100001 );\n\n      {\n         const auto [ a, b, c ] = connection->execute( \"SELECT a, b, c FROM tao_table_writer_test WHERE b IS NULL\" ).tuple< unsigned, std::optional< double >, std::optional< std::string > >();\n\n         TEST_ASSERT_MESSAGE( \"checking 'a' value\", a == 123456 );\n         TEST_ASSERT_MESSAGE( \"checking 'b' value\", !b );\n         TEST_ASSERT_MESSAGE( \"checking 'c' value\", c == \"EUR\\nUSD\\\"FOO\\\\BAR\" );\n      }\n\n      TEST_THROWS( tao::pq::table_writer( connection->direct(), \"SELECT 42\" ) );\n      TEST_THROWS( tao::pq::table_writer( connection->direct(), \"\" ) );\n      TEST_THROWS( tao::pq::table_writer( connection->direct(), \"COPY tao_table_writer_test ( a, b, c, d ) FROM STDIN\" ) );\n      TEST_THROWS( tao::pq::table_writer( connection->direct(), \"COPY tao_table_writer_test ( a, b, c ) TO STDOUT\" ) );\n\n      TEST_THROWS( connection->execute( \"COPY tao_table_writer_test ( a, b, c ) FROM STDIN\" ) );\n\n      TEST_THROWS_MESSAGE( \"mixed usage test #1\", {\n         const auto tr = connection->direct();\n         const tao::pq::table_writer tw2( tr, \"COPY tao_table_writer_test ( a, b, c ) FROM STDIN\" );\n         tr->execute( \"SELECT 42\" );\n      } );\n\n      TEST_THROWS_MESSAGE( \"mixed usage test #2\", {\n         const auto tr = connection->transaction();\n         const tao::pq::table_writer tw2( tr, \"COPY tao_table_writer_test ( a, b, c ) FROM STDIN\" );\n         tr->execute( \"SELECT 42\" );\n      } );\n\n      connection->execute( \"DROP TABLE tao_table_writer_test\" );\n      connection->execute( \"CREATE TABLE tao_table_writer_test ( a INTEGER NOT NULL, b DOUBLE PRECISION, c TEXT )\" );\n      {\n         tao::pq::table_writer tw2( connection->direct(), \"COPY tao_table_writer_test ( a, b, c ) FROM STDIN\" );\n         tw2.insert_raw( \"1\\t0\\tXXX\\n\" );\n         tw2.commit();\n      }\n      TEST_ASSERT( connection->execute( \"SELECT COUNT(*) FROM tao_table_writer_test\" ).as< std::size_t >() == 1 );\n      {\n         tao::pq::table_writer tw2( connection->direct(), \"COPY tao_table_writer_test ( a, b, c ) FROM STDIN\" );\n         tw2.insert_raw( \"2\\t0\\tXXX\\n\" );\n      }\n      TEST_ASSERT( connection->execute( \"SELECT COUNT(*) FROM tao_table_writer_test\" ).as< std::size_t >() == 1 );\n\n      connection->execute( \"DROP TABLE tao_table_writer_test\" );\n      connection->execute( \"CREATE TABLE tao_table_writer_test ( a INTEGER NOT NULL, b DOUBLE PRECISION, c TEXT )\" );\n      {\n         tao::pq::table_writer tw2( connection->direct(), \"COPY tao_table_writer_test ( a, b, c ) FROM STDIN\" );\n         tw2.insert_raw( \"3\\t0\\tXXX\\n\" );\n         PQclear( PQexec( connection->underlying_raw_ptr(), \"SELECT 42\" ) );\n         TEST_THROWS( tw2.commit() );\n      }\n      {\n         tao::pq::table_writer tw2( connection->direct(), \"COPY tao_table_writer_test ( a, b, c ) FROM STDIN\" );\n         tw2.insert_raw( \"4\\t0\\tXXX\\n\" );\n         PQclear( PQexec( connection->underlying_raw_ptr(), \"SELECT 42\" ) );\n         TEST_THROWS( tw2.insert_raw( \"5\\t0\\tXXX\\n\" ) );\n      }\n\n      connection->execute( \"DROP TABLE tao_table_writer_test\" );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/traits.cpp",
    "content": "// Copyright (c) 2020-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <iostream>\n#include <tuple>\n#include <utility>\n\n#include <tao/pq.hpp>\n\nnamespace example\n{\n   struct user\n   {\n      int a, b, c, d;\n\n      explicit user( const int i ) noexcept\n         : a( i ), b( i + 1 ), c( i + 2 ), d( i + 3 )\n      {}\n\n      [[nodiscard]] auto to_taopq() const noexcept\n      {\n         return std::tie( a, b, c, d );\n      }\n\n   private:\n      user( const int in_a, const int in_b, const int in_c, const int in_d ) noexcept\n         : a( in_a ), b( in_b ), c( in_c ), d( in_d )\n      {}\n\n   public:\n      [[nodiscard]] static auto from_taopq( const int in_a, const int in_b, const int in_c, const int in_d ) noexcept\n      {\n         return user( in_a, in_b, in_c, in_d );\n      }\n   };\n\n   struct user2\n   {\n      int a, b, c, d;\n\n      explicit user2( int i ) noexcept\n         : a( i ), b( i + 1 ), c( i + 2 ), d( i + 3 )\n      {}\n\n      user2( const int in_a, const int in_b, const int in_c, const int in_d ) noexcept\n         : a( in_a ), b( in_b ), c( in_c ), d( in_d )\n      {}\n   };\n\n   struct user3\n   {\n      int a, b, c, d;\n\n      explicit user3( int i ) noexcept\n         : a( i ), b( i + 1 ), c( i + 2 ), d( i + 3 )\n      {}\n\n      user3( const int in_a, const int in_b, const int in_c, const int in_d ) noexcept\n         : a( in_a ), b( in_b ), c( in_c ), d( in_d )\n      {}\n   };\n\n   [[nodiscard]] auto to_taopq( const user3& v ) noexcept  // NOLINT(misc-use-internal-linkage)\n   {\n      return std::tie( v.a, v.b, v.c, v.d );\n   }\n\n}  // namespace example\n\ntemplate<>\nstruct tao::pq::bind< example::user2 >\n{\n   [[nodiscard]] static auto to_taopq( const example::user2& v ) noexcept\n   {\n      return std::tie( v.a, v.b, v.c, v.d );\n   }\n\n   [[nodiscard]] static auto from_taopq( const int a, const int b, const int c, const int d ) noexcept\n   {\n      return example::user2( a, b, c, d );\n   }\n};\n\nnamespace\n{\n   void run()\n   {\n      const auto connection = tao::pq::connection::create( tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" ) );\n\n      connection->execute( \"DROP TABLE IF EXISTS tao_traits_test\" );\n      connection->execute( \"CREATE TABLE tao_traits_test ( a INTEGER PRIMARY KEY, b INTEGER, c INTEGER, d INTEGER )\" );\n\n      TEST_EXECUTE( connection->execute( \"INSERT INTO tao_traits_test VALUES ( 1, 2, 3, 4 )\" ) );\n      TEST_EXECUTE( connection->execute( \"INSERT INTO tao_traits_test VALUES ( $1, $2, $3, $4 )\", 2, 3, 4, 5 ) );\n      TEST_EXECUTE( connection->execute( \"INSERT INTO tao_traits_test VALUES ( $1, $2, $3, $4 )\", 3, std::make_pair( 4, 5 ), 6 ) );\n      TEST_EXECUTE( connection->execute( \"INSERT INTO tao_traits_test VALUES ( $1, $2, $3, $4 )\", std::make_tuple( 4, 5 ), std::make_tuple( 6, 7 ) ) );\n      TEST_EXECUTE( connection->execute( \"INSERT INTO tao_traits_test VALUES ( $1, $2, $3, $4 )\", std::make_tuple( 5, std::make_pair( 6, 7 ), 8 ) ) );\n      TEST_EXECUTE( connection->execute( \"INSERT INTO tao_traits_test VALUES ( $1, $2, $3, $4 )\", example::user( 6 ) ) );\n      TEST_EXECUTE( connection->execute( \"INSERT INTO tao_traits_test VALUES ( $1, $2, $3, $4 )\", example::user2( 7 ) ) );\n\n      const auto result = connection->execute( \"SELECT * FROM tao_traits_test\" );\n      TEST_ASSERT( result.size() == 7 );\n\n      for( const auto& row : result ) {\n         {\n            const auto v = row.tuple< int, int, int, int >();\n            TEST_ASSERT( std::get< 1 >( v ) == std::get< 0 >( v ) + 1 );\n            TEST_ASSERT( std::get< 2 >( v ) == std::get< 1 >( v ) + 1 );\n            TEST_ASSERT( std::get< 3 >( v ) == std::get< 2 >( v ) + 1 );\n         }\n         {\n            const auto v = row.tuple< std::tuple< int, int >, std::tuple< int, int > >();\n            TEST_ASSERT( std::get< 1 >( std::get< 0 >( v ) ) == std::get< 0 >( std::get< 0 >( v ) ) + 1 );\n            TEST_ASSERT( std::get< 0 >( std::get< 1 >( v ) ) == std::get< 1 >( std::get< 0 >( v ) ) + 1 );\n            TEST_ASSERT( std::get< 1 >( std::get< 1 >( v ) ) == std::get< 0 >( std::get< 1 >( v ) ) + 1 );\n         }\n         {\n            const auto v = row.tuple< int, std::tuple< int, int >, int >();\n            TEST_ASSERT( std::get< 0 >( std::get< 1 >( v ) ) == std::get< 0 >( v ) + 1 );\n            TEST_ASSERT( std::get< 1 >( std::get< 1 >( v ) ) == std::get< 0 >( std::get< 1 >( v ) ) + 1 );\n            TEST_ASSERT( std::get< 2 >( v ) == std::get< 1 >( std::get< 1 >( v ) ) + 1 );\n         }\n         {\n            const auto [ a, b, c, d ] = row.tuple< int, int, int, int >();\n            TEST_ASSERT( b == a + 1 );\n            TEST_ASSERT( c == b + 1 );\n            TEST_ASSERT( d == c + 1 );\n         }\n      }\n\n      TEST_EXECUTE( connection->execute( \"DELETE FROM tao_traits_test\" ) );\n      TEST_EXECUTE( connection->execute( \"INSERT INTO tao_traits_test VALUES ( $1, $2, $3, $4 )\", example::user( 8 ) ) );\n      {\n         const auto user = connection->execute( \"SELECT * FROM tao_traits_test\" ).as< example::user >();\n         TEST_ASSERT( user.a == 8 );\n         TEST_ASSERT( user.b == 9 );\n         TEST_ASSERT( user.c == 10 );\n         TEST_ASSERT( user.d == 11 );\n      }\n\n      TEST_EXECUTE( connection->execute( \"DELETE FROM tao_traits_test\" ) );\n      TEST_EXECUTE( connection->execute( \"INSERT INTO tao_traits_test VALUES ( $1, $2, $3, $4 )\", example::user2( 9 ) ) );\n      {\n         const auto user = connection->execute( \"SELECT * FROM tao_traits_test\" ).as< example::user2 >();\n         TEST_ASSERT( user.a == 9 );\n         TEST_ASSERT( user.b == 10 );\n         TEST_ASSERT( user.c == 11 );\n         TEST_ASSERT( user.d == 12 );\n      }\n\n      TEST_EXECUTE( connection->execute( \"DELETE FROM tao_traits_test\" ) );\n      TEST_EXECUTE( connection->execute( \"INSERT INTO tao_traits_test VALUES ( $1, $2, $3, $4 )\", example::user3( 10 ) ) );\n      {\n         const auto user = connection->execute( \"SELECT * FROM tao_traits_test\" ).as< example::user >();\n         TEST_ASSERT( user.a == 10 );\n         TEST_ASSERT( user.b == 11 );\n         TEST_ASSERT( user.c == 12 );\n         TEST_ASSERT( user.d == 13 );\n      }\n   }\n\n}  // namespace\n\nauto main() -> int  //NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/integration/transaction.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <iostream>\n#include <memory>\n#include <tuple>\n\n#include <tao/pq.hpp>\n\nnamespace\n{\n   template< typename Connection, typename Transaction >\n   void check_nested( const std::shared_ptr< Connection >& connection, const std::shared_ptr< Transaction >& tr )\n   {\n      TEST_THROWS( connection->direct() );\n      TEST_THROWS( connection->transaction() );\n      TEST_EXECUTE( tr->execute( \"SELECT 42\" ) );\n      {\n         const auto tr2 = tr->subtransaction();\n         TEST_THROWS( tr->subtransaction() );\n         TEST_EXECUTE( tr2->execute( \"SELECT 42\" ) );\n         {\n            const auto tr3 = tr2->subtransaction();\n            TEST_THROWS( tr2->subtransaction() );\n            TEST_EXECUTE( tr3->execute( \"SELECT 42\" ) );\n            TEST_THROWS( tr2->execute( \"SELECT 42\" ) );\n            TEST_EXECUTE( tr3->commit() );\n            TEST_THROWS( tr3->execute( \"SELECT 42\" ) );\n         }\n         TEST_THROWS( tr->execute( \"SELECT 42\" ) );\n         TEST_EXECUTE( tr2->commit() );\n         TEST_THROWS( tr2->execute( \"SELECT 42\" ) );\n         TEST_THROWS( tr2->subtransaction() );\n         TEST_EXECUTE( std::ignore = tr->subtransaction() );\n      }\n      tr->execute( \"SELECT 42\" );\n      {\n         const auto tr2 = tr->subtransaction();\n         TEST_EXECUTE( tr2->execute( \"SELECT 42\" ) );\n         TEST_THROWS( tr->execute( \"SELECT 42\" ) );\n         TEST_EXECUTE( tr2->rollback() );\n         TEST_THROWS( tr2->execute( \"SELECT 42\" ) );\n      }\n      TEST_EXECUTE( tr->execute( \"SELECT 42\" ) );\n      TEST_EXECUTE( tr->commit() );\n      TEST_THROWS( tr->execute( \"SELECT 42\" ) );\n      TEST_EXECUTE( std::ignore = connection->direct() );\n      TEST_EXECUTE( std::ignore = connection->transaction() );\n   }\n\n   void run()\n   {\n      const auto connection = tao::pq::connection::create( tao::pq::internal::getenv( \"TAOPQ_TEST_DATABASE\", \"dbname=template1\" ) );\n\n      connection->execute( \"DROP TABLE IF EXISTS tao_transaction_test\" );\n      connection->execute( \"CREATE TABLE tao_transaction_test ( a INTEGER PRIMARY KEY )\" );\n\n      TEST_ASSERT( connection->execute( \"SELECT * FROM tao_transaction_test\" ).empty() );\n\n      TEST_EXECUTE( connection->execute( \"INSERT INTO tao_transaction_test VALUES ( 1 )\" ) );  // auto-commit\n      TEST_ASSERT( connection->execute( \"SELECT * FROM tao_transaction_test\" ).size() == 1 );\n\n      TEST_EXECUTE( connection->direct()->execute( \"INSERT INTO tao_transaction_test VALUES ( 2 )\" ) );  // auto-commit\n      TEST_ASSERT( connection->execute( \"SELECT * FROM tao_transaction_test\" ).size() == 2 );\n\n      TEST_EXECUTE( connection->transaction()->execute( \"INSERT INTO tao_transaction_test VALUES ( 3 )\" ) );  // not committed\n      TEST_ASSERT( connection->execute( \"SELECT * FROM tao_transaction_test\" ).size() == 2 );\n\n      TEST_EXECUTE( connection->direct()->subtransaction()->execute( \"INSERT INTO tao_transaction_test VALUES ( 3 )\" ) );  // not committed\n      TEST_ASSERT( connection->execute( \"SELECT * FROM tao_transaction_test\" ).size() == 2 );\n\n      TEST_EXECUTE( connection->transaction()->subtransaction()->execute( \"INSERT INTO tao_transaction_test VALUES ( 3 )\" ) );  // not committed\n      TEST_ASSERT( connection->execute( \"SELECT * FROM tao_transaction_test\" ).size() == 2 );\n\n      TEST_THROWS( connection->transaction( tao::pq::access_mode::read_only )->execute( \"INSERT INTO tao_transaction_test VALUES ( 3 )\" ) );\n      TEST_ASSERT( connection->transaction( tao::pq::access_mode::read_only )->execute( \"SELECT * FROM tao_transaction_test\" ).size() == 2 );\n\n      TEST_THROWS_MESSAGE( \"THROWS connection->transaction()\", const auto tr = connection->transaction(); std::ignore = connection->transaction() );\n      TEST_THROWS_MESSAGE( \"THROWS connection->direct()\", const auto tr = connection->transaction(); std::ignore = connection->direct() );\n      TEST_THROWS_MESSAGE( \"THROWS connection->transaction()\", const auto tr = connection->direct(); std::ignore = connection->transaction() );\n      TEST_THROWS_MESSAGE( \"THROWS connection->direct()\", const auto tr = connection->direct(); std::ignore = connection->direct() );\n\n      TEST_THROWS_MESSAGE( \"THROWS tr->subtransaction()\", const auto tr = connection->transaction(); const auto st = tr->subtransaction(); std::ignore = tr->subtransaction() );\n      TEST_THROWS_MESSAGE( \"THROWS tr->subtransaction()\", const auto tr = connection->direct(); const auto st = tr->subtransaction(); std::ignore = tr->subtransaction() );\n\n      TEST_EXECUTE( std::ignore = connection->direct() );\n      TEST_EXECUTE( connection->direct()->commit() );\n      TEST_EXECUTE( connection->direct()->rollback() );\n\n      TEST_EXECUTE( std::ignore = connection->direct()->subtransaction() );\n      TEST_EXECUTE( connection->direct()->subtransaction()->commit() );\n      TEST_EXECUTE( connection->direct()->subtransaction()->rollback() );\n\n      TEST_EXECUTE( std::ignore = connection->direct()->subtransaction()->subtransaction() );\n      TEST_EXECUTE( connection->direct()->subtransaction()->subtransaction()->commit() );\n      TEST_EXECUTE( connection->direct()->subtransaction()->subtransaction()->rollback() );\n\n      TEST_EXECUTE( std::ignore = connection->transaction() );\n      TEST_EXECUTE( connection->transaction()->commit() );\n      TEST_EXECUTE( connection->transaction()->rollback() );\n\n      TEST_EXECUTE( std::ignore = connection->transaction()->subtransaction() );\n      TEST_EXECUTE( connection->transaction()->subtransaction()->commit() );\n      TEST_EXECUTE( connection->transaction()->subtransaction()->rollback() );\n\n      TEST_EXECUTE( std::ignore = connection->transaction()->subtransaction()->subtransaction() );\n      TEST_EXECUTE( connection->transaction()->subtransaction()->subtransaction()->commit() );\n      TEST_EXECUTE( connection->transaction()->subtransaction()->subtransaction()->rollback() );\n\n      TEST_EXECUTE( std::ignore = connection->transaction( tao::pq::isolation_level::serializable ) );\n      TEST_EXECUTE( std::ignore = connection->transaction( tao::pq::isolation_level::repeatable_read ) );\n      TEST_EXECUTE( std::ignore = connection->transaction( tao::pq::isolation_level::read_committed ) );\n      TEST_EXECUTE( std::ignore = connection->transaction( tao::pq::isolation_level::read_uncommitted ) );\n\n      TEST_EXECUTE( std::ignore = connection->transaction( tao::pq::access_mode::read_write ) );\n      TEST_EXECUTE( std::ignore = connection->transaction( tao::pq::access_mode::read_only ) );\n\n      TEST_EXECUTE( check_nested( connection, connection->direct() ) );\n      TEST_EXECUTE( check_nested( connection, connection->transaction() ) );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/unit/getenv.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/getenv.hpp\"\n#include \"utils/macros.hpp\"\n\n#include <exception>\n#include <iostream>\n\nnamespace\n{\n   void run()\n   {\n      TEST_ASSERT( !tao::pq::internal::getenv( \"PATH\" ).empty() );\n      TEST_THROWS( tao::pq::internal::getenv( \"TAOPQ_DOESNOTEXIST\" ) );\n\n      TEST_ASSERT( !tao::pq::internal::getenv( \"PATH\", \"\" ).empty() );\n      TEST_ASSERT( tao::pq::internal::getenv( \"TAOPQ_DOESNOTEXIST\", \"\" ).empty() );\n      TEST_ASSERT( tao::pq::internal::getenv( \"TAOPQ_DOESNOTEXIST\", \"DEFAULT VALUE\" ) == \"DEFAULT VALUE\" );\n   }\n\n}  // namespace\n\nauto main() -> int\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/unit/parameter_type.cpp",
    "content": "// Copyright (c) 2023-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <array>\n#include <list>\n#include <optional>\n#include <set>\n#include <span>\n#include <string>\n#include <string_view>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include <tao/pq.hpp>\n\nstatic_assert( !tao::pq::parameter_type< void > );\n\nstatic_assert( tao::pq::parameter_type< decltype( tao::pq::null ) > );\n\nstatic_assert( tao::pq::parameter_type< bool > );\n\nstatic_assert( tao::pq::parameter_type< char > );\nstatic_assert( tao::pq::parameter_type< signed char > );\nstatic_assert( tao::pq::parameter_type< unsigned char > );\n\nstatic_assert( tao::pq::parameter_type< short > );\nstatic_assert( tao::pq::parameter_type< unsigned short > );\nstatic_assert( tao::pq::parameter_type< int > );\nstatic_assert( tao::pq::parameter_type< unsigned > );\nstatic_assert( tao::pq::parameter_type< long > );\nstatic_assert( tao::pq::parameter_type< unsigned long > );\nstatic_assert( tao::pq::parameter_type< long long > );\nstatic_assert( tao::pq::parameter_type< unsigned long long > );\n\nstatic_assert( tao::pq::parameter_type< float > );\nstatic_assert( tao::pq::parameter_type< double > );\nstatic_assert( tao::pq::parameter_type< long double > );\n\nstatic_assert( tao::pq::parameter_type< const char* > );\nstatic_assert( tao::pq::parameter_type< std::string > );\nstatic_assert( tao::pq::parameter_type< std::string_view > );\n\nstatic_assert( tao::pq::parameter_type< tao::pq::binary > );\nstatic_assert( tao::pq::parameter_type< tao::pq::binary_view > );\n\nstatic_assert( tao::pq::parameter_type< std::span< std::byte > > );\nstatic_assert( tao::pq::parameter_type< std::span< std::byte, 42 > > );\nstatic_assert( tao::pq::parameter_type< std::span< const std::byte > > );\nstatic_assert( tao::pq::parameter_type< std::span< const std::byte, 42 > > );\nstatic_assert( tao::pq::parameter_type< std::vector< std::byte > > );\n\n// optional\nstatic_assert( tao::pq::parameter_type< std::optional< int > > );\nstatic_assert( tao::pq::parameter_type< std::optional< std::string > > );\n\n// pair\nstatic_assert( tao::pq::parameter_type< std::pair< bool, int > > );\nstatic_assert( tao::pq::parameter_type< std::pair< std::string, tao::pq::binary > > );\n\n// tuple\nstatic_assert( !tao::pq::parameter_type< std::tuple<> > );\nstatic_assert( tao::pq::parameter_type< std::tuple< int > > );\nstatic_assert( tao::pq::parameter_type< std::tuple< bool, int, float > > );\nstatic_assert( tao::pq::parameter_type< std::tuple< std::string, tao::pq::binary, unsigned > > );\n\n// array\nstatic_assert( tao::pq::parameter_type< std::array< int, 42 > > );\nstatic_assert( tao::pq::parameter_type< std::array< std::string, 42 > > );\nstatic_assert( tao::pq::parameter_type< std::list< std::string_view > > );\nstatic_assert( tao::pq::parameter_type< std::set< double > > );\nstatic_assert( tao::pq::parameter_type< std::unordered_set< char > > );\nstatic_assert( !tao::pq::parameter_type< std::set< std::pair< int, double > > > );\nstatic_assert( !tao::pq::parameter_type< std::set< std::tuple<> > > );\nstatic_assert( tao::pq::parameter_type< std::set< std::tuple< int > > > );\nstatic_assert( !tao::pq::parameter_type< std::set< std::tuple< bool, int, double > > > );\n\n// note: vector<T> except for T == std::byte are registered as arrays by default\nstatic_assert( tao::pq::parameter_type< std::vector< bool > > );\nstatic_assert( tao::pq::parameter_type< std::vector< unsigned long long > > );\n\nstatic_assert( tao::pq::parameter_type< std::vector< std::set< double > > > );\nstatic_assert( tao::pq::parameter_type< std::set< std::vector< double > > > );\n\n// aggregate\nnamespace example\n{\n   struct user\n   {\n      std::string name;\n      int age;\n      std::string planet;\n   };\n\n   struct user2\n   {\n      std::string name;\n      int age;\n      std::string planet;\n   };\n\n}  // namespace example\n\ntemplate<>\ninline constexpr bool tao::pq::is_aggregate< example::user > = true;\n\nstatic_assert( tao::pq::parameter_type< example::user > );\nstatic_assert( !tao::pq::parameter_type< example::user2 > );  // not registered\n\nauto main() -> int\n{\n   return 0;\n}\n"
  },
  {
    "path": "test/unit/resize_uninitialized.cpp",
    "content": "// Copyright (c) 2021-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include \"utils/macros.hpp\"\n\n#include <cstddef>\n#include <exception>\n#include <iostream>\n#include <string>\n#include <vector>\n\n#include <tao/pq/internal/resize_uninitialized.hpp>\n\nnamespace\n{\n   void test( std::string& s, const std::size_t size )\n   {\n      tao::pq::internal::resize_uninitialized( s, size );\n      TEST_ASSERT( s.size() == size );\n      TEST_ASSERT( s[ size ] == '\\0' );\n   }\n\n   void run()\n   {\n      std::string s = \"hello\";\n      test( s, 2 );\n      TEST_ASSERT( s[ 0 ] == 'h' );\n      TEST_ASSERT( s[ 1 ] == 'e' );\n\n      test( s, 5 );\n      TEST_ASSERT( s[ 0 ] == 'h' );\n      TEST_ASSERT( s[ 1 ] == 'e' );\n\n      test( s, 32 );\n      TEST_ASSERT( s[ 0 ] == 'h' );\n      TEST_ASSERT( s[ 1 ] == 'e' );\n\n      test( s, 1000000000 );\n      TEST_ASSERT( s[ 0 ] == 'h' );\n      TEST_ASSERT( s[ 1 ] == 'e' );\n\n      test( s, 2 );\n      TEST_ASSERT( s[ 0 ] == 'h' );\n      TEST_ASSERT( s[ 1 ] == 'e' );\n\n      std::vector< std::byte > v = { static_cast< std::byte >( 42 ), static_cast< std::byte >( 69 ) };\n      TEST_ASSERT( v.size() == 2 );\n      TEST_ASSERT( v[ 0 ] == static_cast< std::byte >( 42 ) );\n      TEST_ASSERT( v[ 1 ] == static_cast< std::byte >( 69 ) );\n\n      tao::pq::internal::resize_uninitialized( v, 5 );\n      TEST_ASSERT( v.size() == 5 );\n      TEST_ASSERT( v[ 0 ] == static_cast< std::byte >( 42 ) );\n      TEST_ASSERT( v[ 1 ] == static_cast< std::byte >( 69 ) );\n\n      tao::pq::internal::resize_uninitialized( v, 1000000000 );\n      TEST_ASSERT( v.size() == 1000000000 );\n      TEST_ASSERT( v[ 0 ] == static_cast< std::byte >( 42 ) );\n      TEST_ASSERT( v[ 1 ] == static_cast< std::byte >( 69 ) );\n\n      tao::pq::internal::resize_uninitialized( v, 2 );\n      TEST_ASSERT( v.size() == 2 );\n      TEST_ASSERT( v[ 0 ] == static_cast< std::byte >( 42 ) );\n      TEST_ASSERT( v[ 1 ] == static_cast< std::byte >( 69 ) );\n   }\n\n}  // namespace\n\nauto main() -> int\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "test/unit/result_type.cpp",
    "content": "// Copyright (c) 2023-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#include <array>\n#include <list>\n#include <optional>\n#include <set>\n#include <span>\n#include <string>\n#include <string_view>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include <tao/pq.hpp>\n\nstatic_assert( !tao::pq::result_type< void > );\n\nstatic_assert( tao::pq::result_type< bool > );\n\nstatic_assert( tao::pq::result_type< char > );\nstatic_assert( tao::pq::result_type< signed char > );\nstatic_assert( tao::pq::result_type< unsigned char > );\n\nstatic_assert( tao::pq::result_type< short > );\nstatic_assert( tao::pq::result_type< unsigned short > );\nstatic_assert( tao::pq::result_type< int > );\nstatic_assert( tao::pq::result_type< unsigned > );\nstatic_assert( tao::pq::result_type< long > );\nstatic_assert( tao::pq::result_type< unsigned long > );\nstatic_assert( tao::pq::result_type< long long > );\nstatic_assert( tao::pq::result_type< unsigned long long > );\n\nstatic_assert( tao::pq::result_type< float > );\nstatic_assert( tao::pq::result_type< double > );\nstatic_assert( tao::pq::result_type< long double > );\n\nstatic_assert( tao::pq::result_type< const char* > );\nstatic_assert( tao::pq::result_type< std::string > );\nstatic_assert( tao::pq::result_type< std::string_view > );\n\nstatic_assert( tao::pq::result_type< tao::pq::binary > );\nstatic_assert( !tao::pq::result_type< tao::pq::binary_view > );\n\nstatic_assert( !tao::pq::result_type< std::span< std::byte > > );\nstatic_assert( !tao::pq::result_type< std::span< std::byte, 42 > > );\nstatic_assert( !tao::pq::result_type< std::span< const std::byte > > );\nstatic_assert( !tao::pq::result_type< std::span< const std::byte, 42 > > );\nstatic_assert( tao::pq::result_type< std::vector< std::byte > > );\n\n// optional\nstatic_assert( tao::pq::result_type< std::optional< int > > );\nstatic_assert( tao::pq::result_type< std::optional< std::string > > );\n\n// pair\nstatic_assert( tao::pq::result_type< std::pair< bool, int > > );\nstatic_assert( tao::pq::result_type< std::pair< std::string, tao::pq::binary > > );\n\n// tuple\nstatic_assert( !tao::pq::result_type< std::tuple<> > );\nstatic_assert( tao::pq::result_type< std::tuple< int > > );\nstatic_assert( tao::pq::result_type< std::tuple< bool, int, float > > );\nstatic_assert( tao::pq::result_type< std::tuple< std::string, tao::pq::binary, unsigned > > );\n\n// array\nstatic_assert( !tao::pq::result_type< std::array< int, 42 > > );\nstatic_assert( !tao::pq::result_type< std::array< std::string, 42 > > );\nstatic_assert( tao::pq::result_type< std::list< std::string_view > > );\nstatic_assert( tao::pq::result_type< std::set< double > > );\nstatic_assert( tao::pq::result_type< std::unordered_set< char > > );\nstatic_assert( !tao::pq::result_type< std::set< std::pair< int, double > > > );\nstatic_assert( !tao::pq::result_type< std::set< std::tuple<> > > );\nstatic_assert( tao::pq::result_type< std::set< std::tuple< int > > > );\nstatic_assert( !tao::pq::result_type< std::set< std::tuple< bool, int, double > > > );\n\n// note: vector<T> except for T == std::byte are registered as arrays by default\nstatic_assert( tao::pq::result_type< std::vector< bool > > );\nstatic_assert( tao::pq::result_type< std::vector< unsigned long long > > );\n\nstatic_assert( tao::pq::result_type< std::vector< std::set< double > > > );\nstatic_assert( tao::pq::result_type< std::set< std::vector< double > > > );\n\n// aggregate\nnamespace example\n{\n   struct user\n   {\n      std::string name;\n      int age;\n      std::string planet;\n   };\n\n   struct user2\n   {\n      std::string name;\n      int age;\n      std::string planet;\n   };\n\n}  // namespace example\n\ntemplate<>\ninline constexpr bool tao::pq::is_aggregate< example::user > = true;\n\nstatic_assert( tao::pq::result_type< example::user > );\nstatic_assert( !tao::pq::result_type< example::user2 > );  // not registered\n\nauto main() -> int\n{\n   return 0;\n}\n"
  },
  {
    "path": "test/unit/strtox.cpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#if defined( _WIN32 )\nauto main() -> int {}\n#else\n\n#include \"utils/macros.hpp\"\n\n#include <cmath>\n#include <exception>\n#include <format>\n#include <iostream>\n#include <stdexcept>\n#include <string>\n#include <tuple>\n\n#include <tao/pq/internal/strtox.hpp>\n\nnamespace\n{\n   template< typename T >\n   void reject_floating_point( const char* input )\n   {\n      try {\n         std::ignore = tao::pq::internal::strtof( input );\n         throw std::runtime_error( std::format( \"strtof(): {}\", input ) );  // LCOV_EXCL_LINE\n      }\n      catch( const T& e ) {\n         if( e.what() != \"tao::pq::internal::strtof() failed for input: \" + std::string( input ) ) {\n            throw;  // LCOV_EXCL_LINE\n         }\n      }\n      try {\n         std::ignore = tao::pq::internal::strtod( input );\n         throw std::runtime_error( std::format( \"strtod(): {}\", input ) );  // LCOV_EXCL_LINE\n      }\n      catch( const T& e ) {\n         if( e.what() != \"tao::pq::internal::strtod() failed for input: \" + std::string( input ) ) {\n            throw;  // LCOV_EXCL_LINE\n         }\n      }\n      try {\n         std::ignore = tao::pq::internal::strtold( input );\n         throw std::runtime_error( std::format( \"strtold(): {}\", input ) );  // LCOV_EXCL_LINE\n      }\n      catch( const T& e ) {\n         if( e.what() != \"tao::pq::internal::strtold() failed for input: \" + std::string( input ) ) {\n            throw;  // LCOV_EXCL_LINE\n         }\n      }\n   }\n\n   void run()\n   {\n      TEST_ASSERT( tao::pq::internal::strtof( \"0\" ) == 0 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"1\" ) == 1 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"00\" ) == 0 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"01\" ) == 1 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"0.\" ) == 0 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"1.\" ) == 1 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"0.0\" ) == 0 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"1.0\" ) == 1 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"0.5\" ) == .5 );\n      TEST_ASSERT( tao::pq::internal::strtof( \".5\" ) == .5 );\n      TEST_ASSERT( tao::pq::internal::strtof( \".25\" ) == .25 );\n      TEST_ASSERT( tao::pq::internal::strtof( \".125\" ) == .125 );\n      TEST_ASSERT( tao::pq::internal::strtof( \".0625\" ) == .0625 );\n      TEST_ASSERT( tao::pq::internal::strtof( \".4375\" ) == .4375 );\n\n      TEST_ASSERT( tao::pq::internal::strtof( \"-0\" ) == 0 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-1\" ) == -1 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-00\" ) == 0 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-01\" ) == -1 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-0.\" ) == 0 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-1.\" ) == -1 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-0.0\" ) == 0 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-1.0\" ) == -1 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-0.5\" ) == -.5 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-.5\" ) == -.5 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-.25\" ) == -.25 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-.125\" ) == -.125 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-.0625\" ) == -.0625 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-.4375\" ) == -.4375 );\n\n      TEST_ASSERT( tao::pq::internal::strtof( \"3.1415927410125732421875\" ) == 3.1415927410125732421875 );\n      TEST_ASSERT( tao::pq::internal::strtod( \"3.1415927410125732421875\" ) == 3.1415927410125732421875 );\n      TEST_ASSERT( tao::pq::internal::strtold( \"3.1415927410125732421875\" ) == 3.1415927410125732421875 );\n\n      TEST_ASSERT( tao::pq::internal::strtof( \"0000000000000000000000000000000000000.0000000000000000000000000000000000000\" ) == 0 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"0000000000000000000000000000000000001.0000000000000000000000000000000000000\" ) == 1 );\n\n      TEST_ASSERT( std::isinf( tao::pq::internal::strtof( \"inf\" ) ) );\n      TEST_ASSERT( std::isinf( tao::pq::internal::strtof( \"INF\" ) ) );\n      TEST_ASSERT( std::isinf( tao::pq::internal::strtof( \"infinity\" ) ) );\n      TEST_ASSERT( std::isinf( tao::pq::internal::strtof( \"INFINITY\" ) ) );\n      TEST_ASSERT( std::isinf( tao::pq::internal::strtof( \"-inf\" ) ) );\n      TEST_ASSERT( std::isinf( tao::pq::internal::strtof( \"-INF\" ) ) );\n      TEST_ASSERT( std::isinf( tao::pq::internal::strtof( \"-infinity\" ) ) );\n      TEST_ASSERT( std::isinf( tao::pq::internal::strtof( \"-INFINITY\" ) ) );\n      TEST_ASSERT( std::isnan( tao::pq::internal::strtof( \"nan\" ) ) );\n      TEST_ASSERT( std::isnan( tao::pq::internal::strtof( \"NaN\" ) ) );\n      TEST_ASSERT( std::isnan( tao::pq::internal::strtof( \"NAN\" ) ) );\n\n      TEST_ASSERT( tao::pq::internal::strtof( \"inf\" ) > 0 );\n      TEST_ASSERT( tao::pq::internal::strtof( \"-inf\" ) < 0 );\n\n      reject_floating_point< std::runtime_error >( \"\" );\n      reject_floating_point< std::runtime_error >( \" \" );\n      reject_floating_point< std::runtime_error >( \"+\" );\n      reject_floating_point< std::runtime_error >( \"-\" );\n      reject_floating_point< std::runtime_error >( \" 0\" );\n      reject_floating_point< std::runtime_error >( \"0 \" );\n      reject_floating_point< std::runtime_error >( \"0x\" );\n      reject_floating_point< std::runtime_error >( \" 1\" );\n      reject_floating_point< std::runtime_error >( \"1 \" );\n\n      reject_floating_point< std::overflow_error >( \"1e10000\" );\n      reject_floating_point< std::overflow_error >( \"-1e10000\" );\n      reject_floating_point< std::underflow_error >( \"1e-10000\" );\n      reject_floating_point< std::underflow_error >( \"-1e-10000\" );\n   }\n\n}  // namespace\n\nauto main() -> int  // NOLINT(bugprone-exception-escape)\n{\n   try {\n      run();\n   }\n   // LCOV_EXCL_START\n   catch( const std::exception& e ) {\n      std::cerr << \"exception: \" << e.what() << '\\n';\n      throw;\n   }\n   catch( ... ) {\n      std::cerr << \"unknown exception\\n\";\n      throw;\n   }\n   // LCOV_EXCL_STOP\n}\n\n#endif\n"
  },
  {
    "path": "test/utils/compare.hpp",
    "content": "// Copyright (c) 2024-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef SRC_TEST_COMPARE_HPP  // NOLINT(llvm-header-guard)\n#define SRC_TEST_COMPARE_HPP\n\n// This is an internal header used for unit-tests.\n\n#include <cstddef>\n\nnamespace tao::pq::internal\n{\n   template< typename T, typename U >\n   [[nodiscard]] auto compare( const T& lhs, const U& rhs ) noexcept -> bool\n   {\n      if( lhs.size() != rhs.size() ) {\n         return false;\n      }\n      for( std::size_t i = 0; i != lhs.size(); ++i ) {\n         if( lhs[ i ] != rhs[ i ] ) {\n            return false;\n         }\n      }\n      return true;\n   }\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "test/utils/getenv.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef SRC_TEST_GETENV_HPP  // NOLINT(llvm-header-guard)\n#define SRC_TEST_GETENV_HPP\n\n// This is an internal header used for unit-tests.\n\n#include <cstdlib>\n#include <format>\n#include <stdexcept>\n#include <string>\n\n#if defined( _MSC_VER )\n#include <memory>\n#endif\n\nnamespace tao::pq::internal\n{\n#if defined( _MSC_VER )\n\n   [[nodiscard]] inline auto getenv( const std::string& name ) -> std::string\n   {\n      char* buf = nullptr;\n      std::size_t sz = 0;\n      if( _dupenv_s( &buf, &sz, name.c_str() ) == 0 && buf != nullptr ) {\n         const std::unique_ptr< char, decltype( &std::free ) > up( buf, &std::free );\n         return std::string( up.get(), sz );\n      }\n      throw std::runtime_error( std::format( \"environment variable not found: {}\", name ) );\n   }\n\n   [[nodiscard]] inline auto getenv( const std::string& name, const std::string& default_value ) -> std::string\n   {\n      char* buf = nullptr;\n      std::size_t sz = 0;\n      if( _dupenv_s( &buf, &sz, name.c_str() ) == 0 && buf != nullptr ) {\n         const std::unique_ptr< char, decltype( &std::free ) > up( buf, &std::free );\n         return std::string( up.get(), sz );\n      }\n      return default_value;\n   }\n\n#else\n\n   [[nodiscard]] inline auto getenv( const std::string& name ) -> std::string\n   {\n      const char* result = std::getenv( name.c_str() );\n      return ( result != nullptr ) ? result : throw std::runtime_error( std::format( \"environment variable not found: {}\", name ) );\n   }\n\n   [[nodiscard]] inline auto getenv( const std::string& name, const std::string& default_value ) -> std::string\n   {\n      const char* result = std::getenv( name.c_str() );\n      return ( result != nullptr ) ? result : default_value;\n   }\n\n#endif\n\n}  // namespace tao::pq::internal\n\n#endif\n"
  },
  {
    "path": "test/utils/macros.hpp",
    "content": "// Copyright (c) 2016-2026 Daniel Frey and Dr. Colin Hirsch\n// Distributed under the Boost Software License, Version 1.0.\n// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)\n\n#ifndef SRC_TEST_MACROS_HPP  // NOLINT(llvm-header-guard)\n#define SRC_TEST_MACROS_HPP\n\n// This is an internal header used for unit-tests.\n\n#include <cstdlib>\n#include <iostream>\n\n#include <tao/pq/exception.hpp>\n#include <tao/pq/internal/demangle.hpp>\n\n#define STRINGIFY_INTERNAL( ... ) #__VA_ARGS__\n#define STRINGIFY( ... ) STRINGIFY_INTERNAL( __VA_ARGS__ )\n#define FILE_AND_LINE __FILE__ \":\" STRINGIFY( __LINE__ )\n\n#define TEST_EXECUTE_MESSAGE( MeSSaGe, ... )                             \\\n   do {                                                                  \\\n      std::cout << \"TEST [ \" MeSSaGe << \" ] in [ \" FILE_AND_LINE \" ]\\n\"; \\\n      __VA_ARGS__;                                                       \\\n   } while( false )\n\n#define TEST_FAILED                                          \\\n   do {                                                      \\\n      std::cerr << \"TEST FAILED in [ \" FILE_AND_LINE \" ]\\n\"; \\\n      std::exit( 1 );                                        \\\n   } while( false )\n\n#define TEST_ASSERT_MESSAGE( MeSSaGe, ... )       \\\n   TEST_EXECUTE_MESSAGE(                          \\\n      MeSSaGe,                                    \\\n      if( !static_cast< bool >( __VA_ARGS__ ) ) { \\\n         TEST_FAILED;                             \\\n      } )\n\n#define TEST_THROWS_MESSAGE( MeSSaGe, ... )                                                   \\\n   TEST_EXECUTE_MESSAGE(                                                                      \\\n      MeSSaGe,                                                                                \\\n      try {                                                                                   \\\n         __VA_ARGS__;                                                                         \\\n         TEST_FAILED;                                                                         \\\n      } catch( const tao::pq::sql_error& e ) {                                                \\\n         std::cout << \"TEST caught [ \" << tao::pq::internal::demangle( typeid( e ) ) << \" ] \" \\\n                   << \"with SQLSTATE [ \" << e.sqlstate << \" ] \"                               \\\n                   << \"and [ \" << e.what() << \" ] in [ \" FILE_AND_LINE \" ]\\n\";                \\\n      } catch( const std::exception& e ) {                                                    \\\n         std::cout << \"TEST caught [ \" << tao::pq::internal::demangle( typeid( e ) ) << \" ] \" \\\n                   << \"with [ \" << e.what() << \" ] in [ \" FILE_AND_LINE \" ]\\n\";               \\\n      } catch( ... ) {                                                                        \\\n         std::cout << \"TEST caught unknown exception in [ \" FILE_AND_LINE \" ]\\n\";             \\\n      } )\n\n#define TEST_EXECUTE( ... ) TEST_EXECUTE_MESSAGE( \"EXECUTE \" #__VA_ARGS__, __VA_ARGS__ )\n#define TEST_ASSERT( ... ) TEST_ASSERT_MESSAGE( \"ASSERT \" #__VA_ARGS__, __VA_ARGS__ )\n#define TEST_THROWS( ... ) TEST_THROWS_MESSAGE( \"THROWS \" #__VA_ARGS__, (void)__VA_ARGS__ )\n\n#endif\n"
  }
]