libpqxx  7.7.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-2022, 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 #if !defined(PQXX_HEADER_PRE)
17 # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
18 #endif
19 
20 #include "pqxx/separated_list.hxx"
21 #include "pqxx/transaction_base.hxx"
22 
23 
24 namespace pqxx
25 {
27 
80 class PQXX_LIBEXPORT stream_to : transaction_focus
81 {
82 public:
84 
105  transaction_base &tx, std::string_view path, std::string_view columns = "")
106  {
107  return {tx, path, columns};
108  }
109 
111 
120  static stream_to table(
121  transaction_base &tx, table_path path,
122  std::initializer_list<std::string_view> columns = {})
123  {
124  auto const &conn{tx.conn()};
125  return raw_table(tx, conn.quote_table(path), conn.quote_columns(columns));
126  }
127 
128 #if defined(PQXX_HAVE_CONCEPTS)
129 
137  template<PQXX_CHAR_STRINGS_ARG COLUMNS>
138  static stream_to
139  table(transaction_base &tx, table_path path, COLUMNS const &columns)
140  {
141  auto const &conn{tx.conn()};
142  return stream_to::raw_table(
143  tx, conn.quote_table(path), tx.conn().quote_columns(columns));
144  }
145 
147 
154  template<PQXX_CHAR_STRINGS_ARG COLUMNS>
155  static stream_to
156  table(transaction_base &tx, std::string_view path, COLUMNS const &columns)
157  {
158  return stream_to::raw_table(tx, path, tx.conn().quote_columns(columns));
159  }
160 #endif // PQXX_HAVE_CONCEPTS
161 
163 
172  [[deprecated("Use table() or raw_table() factory.")]] stream_to(
173  transaction_base &tx, std::string_view table_name) :
174  stream_to{tx, table_name, ""sv}
175  {}
176 
178 
180  template<typename Columns>
181  [[deprecated("Use table() or raw_table() factory.")]] stream_to(
182  transaction_base &, std::string_view table_name, Columns const &columns);
183 
185 
187  template<typename Iter>
188  [[deprecated("Use table() or raw_table() factory.")]] stream_to(
189  transaction_base &, std::string_view table_name, Iter columns_begin,
190  Iter columns_end);
191 
192  ~stream_to() noexcept;
193 
195  [[nodiscard]] constexpr operator bool() const noexcept
196  {
197  return not m_finished;
198  }
200  [[nodiscard]] constexpr bool operator!() const noexcept
201  {
202  return m_finished;
203  }
204 
206 
212  void complete();
213 
215 
224  template<typename Row> stream_to &operator<<(Row const &row)
225  {
226  write_row(row);
227  return *this;
228  }
229 
231 
236 
238 
244  template<typename Row> void write_row(Row const &row)
245  {
246  fill_buffer(row);
247  write_buffer();
248  }
249 
251 
254  template<typename... Ts> void write_values(Ts const &...fields)
255  {
256  fill_buffer(fields...);
257  write_buffer();
258  }
259 
260 private:
262  stream_to(
263  transaction_base &tx, std::string_view path, std::string_view columns);
264 
265  bool m_finished = false;
266 
268  std::string m_buffer;
269 
271  std::string m_field_buf;
272 
274  internal::glyph_scanner_func *m_scanner;
275 
277  void write_raw_line(std::string_view);
278 
280 
282  void write_buffer();
283 
285  static constexpr std::string_view null_field{"\\N\t"};
286 
288  template<typename T>
289  static std::enable_if_t<nullness<T>::always_null, std::size_t>
290  estimate_buffer(T const &)
291  {
292  return std::size(null_field);
293  }
294 
296 
299  template<typename T>
300  static std::enable_if_t<not nullness<T>::always_null, std::size_t>
301  estimate_buffer(T const &field)
302  {
303  return is_null(field) ? std::size(null_field) : size_buffer(field);
304  }
305 
307  void escape_field_to_buffer(std::string_view data);
308 
310 
316  template<typename Field>
317  std::enable_if_t<not nullness<Field>::always_null>
318  append_to_buffer(Field const &f)
319  {
320  // We append each field, terminated by a tab. That will leave us with
321  // one tab too many, assuming we write any fields at all; we remove that
322  // at the end.
323  if (is_null(f))
324  {
325  // Easy. Append null and tab in one go.
326  m_buffer.append(null_field);
327  }
328  else
329  {
330  // Convert f into m_buffer.
331 
332  using traits = string_traits<Field>;
333  auto const budget{estimate_buffer(f)};
334  auto const offset{std::size(m_buffer)};
335 
336  if constexpr (std::is_arithmetic_v<Field>)
337  {
338  // Specially optimised for "safe" types, which never need any
339  // escaping. Convert straight into m_buffer.
340 
341  // The budget we get from size_buffer() includes room for the trailing
342  // zero, which we must remove. But we're also inserting tabs between
343  // fields, so we re-purpose the extra byte for that.
344  auto const total{offset + budget};
345  m_buffer.resize(total);
346  auto const data{m_buffer.data()};
347  char *const end{traits::into_buf(data + offset, data + total, f)};
348  *(end - 1) = '\t';
349  // Shrink to fit. Keep the tab though.
350  m_buffer.resize(static_cast<std::size_t>(end - data));
351  }
352  else if constexpr (
353  std::is_same_v<Field, std::string> or
354  std::is_same_v<Field, std::string_view> or
355  std::is_same_v<Field, zview>)
356  {
357  // This string may need escaping.
358  m_field_buf.resize(budget);
359  escape_field_to_buffer(f);
360  }
361  else
362  {
363  // This field needs to be converted to a string, and after that,
364  // escaped as well.
365  m_field_buf.resize(budget);
366  auto const data{m_field_buf.data()};
367  escape_field_to_buffer(
368  traits::to_buf(data, data + std::size(m_field_buf), f));
369  }
370  }
371  }
372 
374 
380  template<typename Field>
381  std::enable_if_t<nullness<Field>::always_null>
382  append_to_buffer(Field const &)
383  {
384  m_buffer.append(null_field);
385  }
386 
388  template<typename Container>
389  std::enable_if_t<not std::is_same_v<typename Container::value_type, char>>
390  fill_buffer(Container const &c)
391  {
392  // To avoid unnecessary allocations and deallocations, we run through c
393  // twice: once to determine how much buffer space we may need, and once to
394  // actually write it into the buffer.
395  std::size_t budget{0};
396  for (auto const &f : c) budget += estimate_buffer(f);
397  m_buffer.reserve(budget);
398  for (auto const &f : c) append_to_buffer(f);
399  }
400 
402  template<typename Tuple, std::size_t... indexes>
403  static std::size_t
404  budget_tuple(Tuple const &t, std::index_sequence<indexes...>)
405  {
406  return (estimate_buffer(std::get<indexes>(t)) + ...);
407  }
408 
410  template<typename Tuple, std::size_t... indexes>
411  void append_tuple(Tuple const &t, std::index_sequence<indexes...>)
412  {
413  (append_to_buffer(std::get<indexes>(t)), ...);
414  }
415 
417  template<typename... Elts> void fill_buffer(std::tuple<Elts...> const &t)
418  {
419  using indexes = std::make_index_sequence<sizeof...(Elts)>;
420 
421  m_buffer.reserve(budget_tuple(t, indexes{}));
422  append_tuple(t, indexes{});
423  }
424 
426  template<typename... Ts> void fill_buffer(const Ts &...fields)
427  {
428  (..., append_to_buffer(fields));
429  }
430 
431  constexpr static std::string_view s_classname{"stream_to"};
432 };
433 
434 
435 template<typename Columns>
437  transaction_base &tx, std::string_view table_name, Columns const &columns) :
438  stream_to{tx, table_name, std::begin(columns), std::end(columns)}
439 {}
440 
441 
442 template<typename Iter>
444  transaction_base &tx, std::string_view table_name, Iter columns_begin,
445  Iter columns_end) :
446  stream_to{
447  tx,
448  tx.quote_name(
449  table_name,
450  separated_list(",", columns_begin, columns_end, [&tx](auto col) {
451  return tx.quote_name(*col);
452  }))}
453 {}
454 } // namespace pqxx
455 #endif
Stream data from the database.
Definition: stream_from.hxx:73
Base class for things that monopolise a transaction&#39;s attention.
Definition: transaction_focus.hxx:28
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:26
static stream_to table(transaction_base &tx, table_path path, std::initializer_list< std::string_view > columns={})
Create a stream_to writing to a named table and columns.
Definition: stream_to.hxx:120
std::string quote_columns(STRINGS const &columns) const
Quote and comma-separate a series of column names.
Definition: connection.hxx:1225
Reference to one row in a result.
Definition: row.hxx:46
void write_values(Ts const &...fields)
Insert values as a row.
Definition: stream_to.hxx:254
Traits class for use in string conversions.
Definition: strconv.hxx:154
std::string quote_name(std::string_view identifier) const
Escape an SQL identifier for use in a query.
Definition: transaction_base.hxx:220
Reference to a field in a result set.
Definition: field.hxx:34
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:244
std::vector< std::string_view > to_buf(char *here, char const *end, TYPE... value)
Convert multiple values to strings inside a single buffer.
Definition: strconv.hxx:346
Efficiently write data directly to a database table.
Definition: stream_to.hxx:80
constexpr bool operator!() const noexcept
Has this stream been through its concluding complete()?
Definition: stream_to.hxx:200
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:495
Interface definition (and common code) for "transaction" classes.
Definition: transaction_base.hxx:76
stream_to(transaction_base &tx, std::string_view table_name)
Create a stream, without specifying columns.
Definition: stream_to.hxx:172
static stream_to raw_table(transaction_base &tx, std::string_view path, std::string_view columns="")
Stream data to a pre-quoted table and columns.
Definition: stream_to.hxx:104
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:378
stream_to & operator<<(Row const &row)
Insert a row of data.
Definition: stream_to.hxx:224
constexpr connection & conn() const noexcept
The connection in which this transaction lives.
Definition: transaction_base.hxx:527
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:43
std::initializer_list< std::string_view > table_path
Representation of a PostgreSQL table path.
Definition: connection.hxx:124
constexpr bool is_null(TYPE const &value) noexcept
Is value null?
Definition: strconv.hxx:367