Lightweight 0.20261105.1
Loading...
Searching...
No Matches
SqlBinary.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "../SqlColumnTypeDefinitions.hpp"
6#include "BasicStringBinder.hpp"
7#include "Core.hpp"
8
9#include <cassert>
10#include <cstddef>
11#include <cstdint>
12#include <utility>
13#include <vector>
14
15namespace Lightweight
16{
17
18/// @brief Represents a binary data type.
19///
20/// This class is a thin wrapper around std::vector<uint8_t> to represent binary data types efficiently.
21///
22/// @ingroup DataTypes
23class SqlBinary final: public std::vector<uint8_t>
24{
25 public:
26 using std::vector<uint8_t>::vector;
27
28 /// Three-way comparison operator. Compares the byte payload only — the private
29 /// `_indicator` member is ODBC bookkeeping and must not influence equality.
30 constexpr auto operator<=>(SqlBinary const& other) const noexcept
31 {
32 return static_cast<std::vector<uint8_t> const&>(*this) <=> static_cast<std::vector<uint8_t> const&>(other);
33 }
34
35 /// Equality operator derived from the spaceship comparison above.
36 constexpr bool operator==(SqlBinary const& other) const noexcept
37 {
38 return static_cast<std::vector<uint8_t> const&>(*this) == static_cast<std::vector<uint8_t> const&>(other);
39 }
40
41 private:
42 friend struct SqlDataBinder<SqlBinary>;
43
44 mutable SQLLEN _indicator = 0;
45};
46
47template <>
48struct SqlDataBinder<SqlBinary>
49{
50 static constexpr auto ColumnType = SqlColumnTypeDefinitions::Binary { 255 };
51
52 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
53 SQLUSMALLINT column,
54 SqlBinary const& value,
55 SqlDataBinderCallback& /*cb*/) noexcept
56 {
57 value._indicator = static_cast<SQLLEN>(value.size());
58 // An empty payload binds a 1-byte sentinel buffer and `ColumnSize == 1`; the
59 // indicator still carries the real byte count (0), so the server stores an
60 // empty BLOB. Required because psqlODBC rejects a null `ParameterValuePtr`
61 // (HY000) and the MSSQL ODBC driver rejects `ColumnSize == 0` (HY104).
62 static constexpr std::uint8_t emptySentinel = 0;
63 bool const isEmpty = value.empty();
64 auto const* dataPtr = isEmpty ? &emptySentinel : value.data();
65 SQLULEN const columnSize = isEmpty ? SQLULEN { 1 } : static_cast<SQLULEN>(value.size());
66 // A long binary parameter is sent to MS SQL Server as `image`, which cannot be compared, so
67 // a SqlBinary in a WHERE clause failed with "varbinary and image are incompatible" (#618).
68 // Mirrors SqlDynamicBinary: plain VARBINARY up to 8000 bytes, SQL Server's varbinary limit.
69 constexpr std::size_t MaxVarBinaryBytes = 8000;
70 auto const sqlType = static_cast<SQLSMALLINT>(value.size() > MaxVarBinaryBytes ? SQL_LONGVARBINARY : SQL_VARBINARY);
71 return SQLBindParameter(
72 stmt, column, SQL_PARAM_INPUT, SQL_C_BINARY, sqlType, columnSize, 0, (SQLPOINTER) dataPtr, 0, &value._indicator);
73 }
74
75 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN OutputColumn(
76 SQLHSTMT stmt, SQLUSMALLINT column, SqlBinary* result, SQLLEN* indicator, SqlDataBinderCallback& cb) noexcept
77 {
78 if (result->empty())
79 result->resize(255);
80
81 cb.PlanPostProcessOutputColumn([stmt, column, result, indicator]() {
82 if (*indicator == SQL_NULL_DATA)
83 // The data is NULL.
84 result->clear();
85 else if (*indicator == SQL_NO_TOTAL)
86 // We have a truncation and the server does not know how much data is left.
87 result->resize(result->size() - 1);
88 else if (std::cmp_less_equal(*indicator, static_cast<SQLLEN>(result->size())))
89 result->resize(static_cast<size_t>(*indicator));
90 else
91 {
92 // We have a truncation and the server knows how much data is left.
93 // Extend the buffer and fetch the rest via SQLGetData.
94
95 auto const totalCharsRequired = *indicator;
96 result->resize(static_cast<size_t>(totalCharsRequired) + 1);
97 auto const sqlResult =
98 SQLGetData(stmt, column, SQL_C_BINARY, (SQLPOINTER) result->data(), totalCharsRequired + 1, indicator);
99 (void) sqlResult;
100 assert(SQL_SUCCEEDED(sqlResult));
101 assert(*indicator == totalCharsRequired);
102 result->resize(static_cast<size_t>(totalCharsRequired));
103 }
104 });
105
106 return SQLBindCol(stmt, column, SQL_C_BINARY, (SQLPOINTER) result->data(), 255, indicator);
107 }
108
109 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN GetColumn(SQLHSTMT stmt,
110 SQLUSMALLINT column,
111 SqlBinary* result,
112 SQLLEN* indicator,
113 SqlDataBinderCallback const& /*cb*/) noexcept
114 {
115 if (result->empty())
116 result->resize(255);
117
118 return detail::GetRawColumnArrayData<SQL_C_BINARY>(stmt, column, result, indicator);
119 }
120
121 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(SqlBinary const& value)
122 {
123 return std::format("SqlBinary(size={})", value.size());
124 }
125};
126
127} // namespace Lightweight
Represents a binary data type.
Definition SqlBinary.hpp:24
constexpr auto operator<=>(SqlBinary const &other) const noexcept
Definition SqlBinary.hpp:30
constexpr bool operator==(SqlBinary const &other) const noexcept
Equality operator derived from the spaceship comparison above.
Definition SqlBinary.hpp:36