userver: userver/storages/postgres/io/field_buffer.hpp Source File
Loading...
Searching...
No Matches
field_buffer.hpp
1#pragma once
2
3#include <userver/storages/postgres/exceptions.hpp>
4#include <userver/storages/postgres/io/buffer_io.hpp>
5#include <userver/storages/postgres/io/integral_types.hpp>
6#include <userver/storages/postgres/io/nullable_traits.hpp>
7
8USERVER_NAMESPACE_BEGIN
9
10namespace storages::postgres::io {
11
12inline constexpr FieldBuffer FieldBuffer::GetSubBuffer(std::size_t offset, std::size_t size, BufferCategory cat) const {
13 const auto* new_buffer_start = buffer + offset;
14 if (offset > length) {
15 throw InvalidInputBufferSize(fmt::format("Offset {} requested for a buffer of size {}", offset, length));
16 }
17 size = size == npos ? length - offset : size;
18 if (offset + size > length) {
20 fmt::format("Attempt to read {} bytes more than was sent by server", offset + size - length)
21 );
22 }
24 cat = category;
25 }
26 return {is_null, cat, size, new_buffer_start};
27}
28
29template <typename T>
30std::size_t FieldBuffer::Read(T&& value, BufferCategory cat, std::size_t len) {
31 io::ReadBuffer(GetSubBuffer(0, len, cat), std::forward<T>(value));
32 length -= len;
33 buffer += len;
34 return len;
35}
36
37template <typename T>
38std::size_t FieldBuffer::Read(T&& value, const TypeBufferCategory& categories, std::size_t len, BufferCategory cat) {
39 io::ReadBuffer(GetSubBuffer(0, len, cat), std::forward<T>(value), categories);
40 length -= len;
41 buffer += len;
42 return len;
43}
44
45template <typename T>
46std::size_t FieldBuffer::ReadRaw(T&& value, const TypeBufferCategory& categories, BufferCategory cat) {
47 using ValueType = std::decay_t<T>;
48 Integer field_length{0};
49 auto consumed = Read(field_length, BufferCategory::kPlainBuffer);
50 if (field_length == kPgNullBufferSize) {
51 // NULL value
52 traits::GetSetNull<ValueType>::SetNull(std::forward<T>(value));
53 return consumed;
54 } else if (field_length < 0) {
55 // invalid length value
56 throw InvalidInputBufferSize(fmt::format("Negative buffer size value {}", field_length));
57 } else if (field_length == 0) {
58 traits::GetSetNull<ValueType>::SetDefault(std::forward<T>(value));
59 return consumed;
60 } else {
61 return consumed + Read(value, categories, field_length, cat);
62 }
63}
64
65template <typename T>
66std::size_t ReadRawBinary(FieldBuffer buffer, T& value, const TypeBufferCategory& categories) {
67 return buffer.ReadRaw(value, categories);
68}
69
70namespace detail {
71
72template <typename T, typename Buffer, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
73struct FormatterAcceptsReplacementOid : std::false_type {};
74
75template <typename T, typename Buffer>
76struct FormatterAcceptsReplacementOid<
77 T,
78 Buffer,
79 USERVER_NAMESPACE::utils::void_t<
80 decltype(std::declval<T&>()(std::declval<const UserTypes&>(), std::declval<Buffer&>(), std::declval<Oid>()))>>
81 : std::true_type {};
82
83} // namespace detail
84
85template <typename T, typename Buffer>
86void WriteRawBinary(
87 const UserTypes& types,
88 Buffer& buffer,
89 const T& value,
90 [[maybe_unused]] Oid replace_oid = kInvalidOid
91) {
92 traits::CheckFormatter<T>();
93 static constexpr auto size_len = sizeof(Integer);
94 if (traits::GetSetNull<T>::IsNull(value)) {
95 io::WriteBuffer(types, buffer, kPgNullBufferSize);
96 } else {
97 using BufferFormatter = typename traits::IO<T>::FormatterType;
98 using AcceptsReplacementOid = detail::FormatterAcceptsReplacementOid<BufferFormatter, Buffer>;
99 auto len_start = buffer.size();
100 buffer.resize(buffer.size() + size_len);
101 auto size_before = buffer.size();
102 if constexpr (AcceptsReplacementOid{}) {
103 BufferFormatter{value}(types, buffer, replace_oid);
104 } else {
105 io::WriteBuffer(types, buffer, value);
106 }
107 const Integer bytes = buffer.size() - size_before;
108 BufferWriter(bytes)(buffer.begin() + len_start);
109 }
110}
111
112} // namespace storages::postgres::io
113
114USERVER_NAMESPACE_END