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