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) {
19 throw InvalidInputBufferSize(fmt::format("Unconsumed bytes in buffer: {}.", length - offset));
20 }
22 cat = category;
23 }
24 return {is_null, cat, size, new_buffer_start};
25}
26
27template <typename T>
28std::size_t FieldBuffer::Read(T&& value, BufferCategory cat, std::size_t len) {
29 io::ReadBuffer(GetSubBuffer(0, len, cat), std::forward<T>(value));
30 length -= len;
31 buffer += len;
32 return len;
33}
34
35template <typename T>
36std::size_t FieldBuffer::Read(T&& value, const TypeBufferCategory& categories, std::size_t len, BufferCategory cat) {
37 io::ReadBuffer(GetSubBuffer(0, len, cat), std::forward<T>(value), categories);
38 length -= len;
39 buffer += len;
40 return len;
41}
42
43template <typename T>
44std::size_t FieldBuffer::ReadRaw(T&& value, const TypeBufferCategory& categories, BufferCategory cat) {
45 using ValueType = std::decay_t<T>;
46 Integer field_length{0};
47 auto consumed = Read(field_length, BufferCategory::kPlainBuffer);
48 if (field_length == kPgNullBufferSize) {
49 // NULL value
50 traits::GetSetNull<ValueType>::SetNull(std::forward<T>(value));
51 return consumed;
52 } else if (field_length < 0) {
53 // invalid length value
54 throw InvalidInputBufferSize(fmt::format("Negative buffer size value {}", field_length));
55 } else if (field_length == 0) {
56 traits::GetSetNull<ValueType>::SetDefault(std::forward<T>(value));
57 return consumed;
58 } else {
59 return consumed + Read(value, categories, field_length, cat);
60 }
61}
62
63template <typename T>
64std::size_t ReadRawBinary(FieldBuffer buffer, T& value, const TypeBufferCategory& categories) {
65 return buffer.ReadRaw(value, categories);
66}
67
68namespace detail {
69
70template <typename T, typename Buffer, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
71struct FormatterAcceptsReplacementOid : std::false_type {};
72
73template <typename T, typename Buffer>
74struct FormatterAcceptsReplacementOid<
75 T,
76 Buffer,
77 USERVER_NAMESPACE::utils::void_t<decltype(std::declval<T&>(
78 )(std::declval<const UserTypes&>(), std::declval<Buffer&>(), std::declval<Oid>()))>> : std::true_type {};
79
80} // namespace detail
81
82template <typename T, typename Buffer>
83void WriteRawBinary(
84 const UserTypes& types,
85 Buffer& buffer,
86 const T& value,
87 [[maybe_unused]] Oid replace_oid = kInvalidOid
88) {
89 traits::CheckFormatter<T>();
90 static constexpr auto size_len = sizeof(Integer);
91 if (traits::GetSetNull<T>::IsNull(value)) {
92 io::WriteBuffer(types, buffer, kPgNullBufferSize);
93 } else {
94 using BufferFormatter = typename traits::IO<T>::FormatterType;
95 using AcceptsReplacementOid = detail::FormatterAcceptsReplacementOid<BufferFormatter, Buffer>;
96 auto len_start = buffer.size();
97 buffer.resize(buffer.size() + size_len);
98 auto size_before = buffer.size();
99 if constexpr (AcceptsReplacementOid{}) {
100 BufferFormatter{value}(types, buffer, replace_oid);
101 } else {
102 io::WriteBuffer(types, buffer, value);
103 }
104 Integer bytes = buffer.size() - size_before;
105 BufferWriter(bytes)(buffer.begin() + len_start);
106 }
107}
108
109} // namespace storages::postgres::io
110
111USERVER_NAMESPACE_END