libpqxx  7.3.1
stream_to.hxx
1 /* Definition of the pqxx::stream_to class.
2  *
3  * pqxx::stream_to enables optimized batch updates to a database table.
4  *
5  * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/stream_to.hxx instead.
6  *
7  * Copyright (c) 2000-2021, Jeroen T. Vermeulen.
8  *
9  * See COPYING for copyright license. If you did not receive a file called
10  * COPYING with this source code, please notify the distributor of this
11  * mistake, or contact the author.
12  */
13 #ifndef PQXX_H_STREAM_TO
14 #define PQXX_H_STREAM_TO
15 
16 #include "pqxx/compiler-public.hxx"
17 #include "pqxx/internal/compiler-internal-pre.hxx"
18 
19 #include "pqxx/separated_list.hxx"
20 #include "pqxx/transaction_base.hxx"
21 
22 
23 namespace pqxx
24 {
26 
74 class PQXX_LIBEXPORT stream_to : internal::transactionfocus
75 {
76 public:
78 
85  stream_to(transaction_base &, std::string_view table_name);
86 
88  template<typename Columns>
89  stream_to(
90  transaction_base &, std::string_view table_name, Columns const &columns);
91 
93  template<typename Iter>
94  stream_to(
95  transaction_base &, std::string_view table_name, Iter columns_begin,
96  Iter columns_end);
97 
98  ~stream_to() noexcept;
99 
101  [[nodiscard]] operator bool() const noexcept { return not m_finished; }
103  [[nodiscard]] bool operator!() const noexcept { return m_finished; }
104 
106 
112  void complete();
113 
115 
124  template<typename Row> stream_to &operator<<(Row const &row)
125  {
126  write_row(row);
127  return *this;
128  }
129 
131 
136 
138 
144  template<typename Row> void write_row(Row const &row)
145  {
146  fill_buffer(row);
147  write_buffer();
148  }
149 
151 
154  template<typename... Ts> void write_values(const Ts &...fields)
155  {
156  fill_buffer(fields...);
157  write_buffer();
158  }
159 
160 private:
161  bool m_finished = false;
162 
164  std::string m_buffer;
165 
167  std::string m_field_buf;
168 
170  void write_raw_line(std::string_view);
171 
173 
175  void write_buffer();
176 
178  static constexpr std::string_view null_field{"\\N\t"};
179 
181  template<typename T>
182  static std::enable_if_t<nullness<T>::always_null, std::size_t>
183  estimate_buffer(T const &)
184  {
185  return std::size(null_field);
186  }
187 
189 
192  template<typename T>
193  static std::enable_if_t<not nullness<T>::always_null, std::size_t>
194  estimate_buffer(T const &field)
195  {
196  return is_null(field) ? std::size(null_field) : size_buffer(field);
197  }
198 
200  void escape_field_to_buffer(std::string_view);
201 
203 
209  template<typename Field>
210  std::enable_if_t<not nullness<Field>::always_null>
211  append_to_buffer(Field const &f)
212  {
213  // We append each field, terminated by a tab. That will leave us with
214  // one tab too many, assuming we write any fields at all; we remove that
215  // at the end.
216  if (is_null(f))
217  {
218  // Easy. Append null and tab in one go.
219  m_buffer.append(null_field);
220  }
221  else
222  {
223  // Convert f into m_buffer.
224 
225  using traits = string_traits<Field>;
226  auto const budget{estimate_buffer(f)};
227  auto const offset{std::size(m_buffer)};
228 
229  if constexpr (std::is_arithmetic_v<Field>)
230  {
231  // Specially optimised for "safe" types, which never need any
232  // escaping. Convert straight into m_buffer.
233 
234  // The budget we get from size_buffer() includes room for the trailing
235  // zero, which we must remove. But we're also inserting tabs between
236  // fields, so we re-purpose the extra byte for that.
237  auto const total{offset + budget};
238  m_buffer.resize(total);
239  char *const end{traits::into_buf(
240  m_buffer.data() + offset, m_buffer.data() + total, f)};
241  *(end - 1) = '\t';
242  // Shrink to fit. Keep the tab though.
243  m_buffer.resize(static_cast<std::size_t>(end - m_buffer.data()));
244  }
245  else
246  {
247  // TODO: Specialise string/string_view/zview to skip to_buf()!
248  // This field may need escaping. First convert the value into
249  // m_field_buffer, then escape into its final place.
250  m_field_buf.resize(budget);
251  escape_field_to_buffer(traits::to_buf(
252  m_field_buf.data(), m_field_buf.data() + std::size(m_field_buf), f));
253  }
254  }
255  }
256 
258 
264  template<typename Field>
265  std::enable_if_t<nullness<Field>::always_null>
266  append_to_buffer(Field const &)
267  {
268  m_buffer.append(null_field);
269  }
270 
272  template<typename Container> void fill_buffer(Container const &c)
273  {
274  // To avoid unnecessary allocations and deallocations, we run through c
275  // twice: once to determine how much buffer space we may need, and once to
276  // actually write it into the buffer.
277  std::size_t budget{0};
278  for (auto const &f : c) budget += estimate_buffer(f);
279  m_buffer.reserve(budget);
280  for (auto const &f : c) append_to_buffer(f);
281  }
282 
284  template<typename Tuple, std::size_t... indexes>
285  static std::size_t
286  budget_tuple(Tuple const &t, std::index_sequence<indexes...>)
287  {
288  return (estimate_buffer(std::get<indexes>(t)) + ...);
289  }
290 
292  template<typename Tuple, std::size_t... indexes>
293  void append_tuple(Tuple const &t, std::index_sequence<indexes...>)
294  {
295  (append_to_buffer(std::get<indexes>(t)), ...);
296  }
297 
299  template<typename... Elts> void fill_buffer(std::tuple<Elts...> const &t)
300  {
301  using indexes = std::make_index_sequence<sizeof...(Elts)>;
302 
303  m_buffer.reserve(budget_tuple(t, indexes{}));
304  append_tuple(t, indexes{});
305  }
306 
307  void set_up(transaction_base &, std::string_view table_name);
308  void set_up(
309  transaction_base &, std::string_view table_name, std::string_view columns);
310 
312  template<typename... Ts> void fill_buffer(const Ts &...fields)
313  {
314  (..., append_to_buffer(fields));
315  }
316 
317  constexpr static std::string_view s_classname{"stream_to"};
318 };
319 
320 
321 template<typename Columns>
323  transaction_base &tb, std::string_view table_name, Columns const &columns) :
324  stream_to{tb, table_name, std::begin(columns), std::end(columns)}
325 {}
326 
327 
328 template<typename Iter>
330  transaction_base &tb, std::string_view table_name, Iter columns_begin,
331  Iter columns_end) :
332  internal::transactionfocus{tb, s_classname, table_name}
333 {
334  set_up(tb, table_name, separated_list(",", columns_begin, columns_end));
335 }
336 } // namespace pqxx
337 
338 #include "pqxx/internal/compiler-internal-post.hxx"
339 #endif
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:26
std::string separated_list(std::string_view sep, ITER begin, ITER end, ACCESS access)
Represent sequence of values as a string, joined by a given separator.
Definition: separated_list.hxx:40
std::size_t size_buffer(TYPE const &...value) noexcept
Estimate how much buffer space is needed to represent values as a string.
Definition: strconv.hxx:364
std::basic_ostream< CHAR > & operator<<(std::basic_ostream< CHAR > &s, field const &value)
Write a result field to any type of stream.
Definition: field.hxx:480
bool is_null(TYPE const &value) noexcept
Is value null?
Definition: strconv.hxx:353
Reference to one row in a result.
Definition: row.hxx:46
Stream data from the database.
Definition: stream_from.hxx:68
Efficiently write data directly to a database table.
Definition: stream_to.hxx:75
stream_to & operator<<(Row const &row)
Insert a row of data.
Definition: stream_to.hxx:124
void write_values(const Ts &...fields)
Insert values as a row.
Definition: stream_to.hxx:154
stream_to(transaction_base &, std::string_view table_name)
Create a stream, without specifying columns.
Definition: stream_to.cxx:36
void write_row(Row const &row)
Insert a row of data, given in the form of a std::tuple or container.
Definition: stream_to.hxx:144
bool operator!() const noexcept
Has this stream been through its concluding complete()?
Definition: stream_to.hxx:103
Interface definition (and common code) for "transaction" classes.
Definition: transaction_base.hxx:72