libpqxx  7.7.0
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 #include "pqxx/separated_list.hxx"
17 #include "pqxx/transaction_base.hxx"
18 
19 
20 namespace pqxx
21 {
23 
76 class PQXX_LIBEXPORT stream_to : transaction_focus
77 {
78 public:
80 
101  transaction_base &tx, std::string_view path, std::string_view columns = "")
102  {
103  return {tx, path, columns};
104  }
105 
107 
116  static stream_to table(
117  transaction_base &tx, table_path path,
118  std::initializer_list<std::string_view> columns = {})
119  {
120  auto const &conn{tx.conn()};
121  return raw_table(tx, conn.quote_table(path), conn.quote_columns(columns));
122  }
123 
124 #if defined(PQXX_HAVE_CONCEPTS)
125 
133  template<PQXX_CHAR_STRINGS_ARG COLUMNS>
134  static stream_to
135  table(transaction_base &tx, table_path path, COLUMNS const &columns)
136  {
137  auto const &conn{tx.conn()};
138  return stream_to::raw_table(
139  tx, conn.quote_table(path), tx.conn().quote_columns(columns));
140  }
141 
143 
150  template<PQXX_CHAR_STRINGS_ARG COLUMNS>
151  static stream_to
152  table(transaction_base &tx, std::string_view path, COLUMNS const &columns)
153  {
154  return stream_to::raw_table(tx, path, tx.conn().quote_columns(columns));
155  }
156 #endif // PQXX_HAVE_CONCEPTS
157 
159 
168  [[deprecated("Use table() or raw_table() factory.")]] stream_to(
169  transaction_base &tx, std::string_view table_name) :
170  stream_to{tx, table_name, ""sv}
171  {}
172 
174 
176  template<typename Columns>
177  [[deprecated("Use table() or raw_table() factory.")]] stream_to(
178  transaction_base &, std::string_view table_name, Columns const &columns);
179 
181 
183  template<typename Iter>
184  [[deprecated("Use table() or raw_table() factory.")]] stream_to(
185  transaction_base &, std::string_view table_name, Iter columns_begin,
186  Iter columns_end);
187 
188  ~stream_to() noexcept;
189 
191  [[nodiscard]] operator bool() const noexcept { return not m_finished; }
193  [[nodiscard]] bool operator!() const noexcept { return m_finished; }
194 
196 
202  void complete();
203 
205 
214  template<typename Row> stream_to &operator<<(Row const &row)
215  {
216  write_row(row);
217  return *this;
218  }
219 
221 
226 
228 
234  template<typename Row> void write_row(Row const &row)
235  {
236  fill_buffer(row);
237  write_buffer();
238  }
239 
241 
244  template<typename... Ts> void write_values(Ts const &...fields)
245  {
246  fill_buffer(fields...);
247  write_buffer();
248  }
249 
250 private:
252  stream_to(
253  transaction_base &tx, std::string_view path, std::string_view columns);
254 
255  bool m_finished = false;
256 
258  std::string m_buffer;
259 
261  std::string m_field_buf;
262 
264  internal::glyph_scanner_func *m_scanner;
265 
267  void write_raw_line(std::string_view);
268 
270 
272  void write_buffer();
273 
274  // C++20: constinit.
276  static constexpr std::string_view null_field{"\\N\t"};
277 
279  template<typename T>
280  static std::enable_if_t<nullness<T>::always_null, std::size_t>
281  estimate_buffer(T const &)
282  {
283  return std::size(null_field);
284  }
285 
287 
290  template<typename T>
291  static std::enable_if_t<not nullness<T>::always_null, std::size_t>
292  estimate_buffer(T const &field)
293  {
294  return is_null(field) ? std::size(null_field) : size_buffer(field);
295  }
296 
298  void escape_field_to_buffer(std::string_view data);
299 
301 
307  template<typename Field>
308  std::enable_if_t<not nullness<Field>::always_null>
309  append_to_buffer(Field const &f)
310  {
311  // We append each field, terminated by a tab. That will leave us with
312  // one tab too many, assuming we write any fields at all; we remove that
313  // at the end.
314  if (is_null(f))
315  {
316  // Easy. Append null and tab in one go.
317  m_buffer.append(null_field);
318  }
319  else
320  {
321  // Convert f into m_buffer.
322 
323  using traits = string_traits<Field>;
324  auto const budget{estimate_buffer(f)};
325  auto const offset{std::size(m_buffer)};
326 
327  if constexpr (std::is_arithmetic_v<Field>)
328  {
329  // Specially optimised for "safe" types, which never need any
330  // escaping. Convert straight into m_buffer.
331 
332  // The budget we get from size_buffer() includes room for the trailing
333  // zero, which we must remove. But we're also inserting tabs between
334  // fields, so we re-purpose the extra byte for that.
335  auto const total{offset + budget};
336  m_buffer.resize(total);
337  auto const data{m_buffer.data()};
338  char *const end{traits::into_buf(data + offset, data + total, f)};
339  *(end - 1) = '\t';
340  // Shrink to fit. Keep the tab though.
341  m_buffer.resize(static_cast<std::size_t>(end - data));
342  }
343  else if constexpr (
344  std::is_same_v<Field, std::string> or
345  std::is_same_v<Field, std::string_view> or
346  std::is_same_v<Field, zview>)
347  {
348  // This string may need escaping.
349  m_field_buf.resize(budget);
350  escape_field_to_buffer(f);
351  }
352  else
353  {
354  // This field needs to be converted to a string, and after that,
355  // escaped as well.
356  m_field_buf.resize(budget);
357  auto const data{m_field_buf.data()};
358  escape_field_to_buffer(
359  traits::to_buf(data, data + std::size(m_field_buf), f));
360  }
361  }
362  }
363 
365 
371  template<typename Field>
372  std::enable_if_t<nullness<Field>::always_null>
373  append_to_buffer(Field const &)
374  {
375  m_buffer.append(null_field);
376  }
377 
379  template<typename Container>
380  std::enable_if_t<not std::is_same_v<typename Container::value_type, char>>
381  fill_buffer(Container const &c)
382  {
383  // To avoid unnecessary allocations and deallocations, we run through c
384  // twice: once to determine how much buffer space we may need, and once to
385  // actually write it into the buffer.
386  std::size_t budget{0};
387  for (auto const &f : c) budget += estimate_buffer(f);
388  m_buffer.reserve(budget);
389  for (auto const &f : c) append_to_buffer(f);
390  }
391 
393  template<typename Tuple, std::size_t... indexes>
394  static std::size_t
395  budget_tuple(Tuple const &t, std::index_sequence<indexes...>)
396  {
397  return (estimate_buffer(std::get<indexes>(t)) + ...);
398  }
399 
401  template<typename Tuple, std::size_t... indexes>
402  void append_tuple(Tuple const &t, std::index_sequence<indexes...>)
403  {
404  (append_to_buffer(std::get<indexes>(t)), ...);
405  }
406 
408  template<typename... Elts> void fill_buffer(std::tuple<Elts...> const &t)
409  {
410  using indexes = std::make_index_sequence<sizeof...(Elts)>;
411 
412  m_buffer.reserve(budget_tuple(t, indexes{}));
413  append_tuple(t, indexes{});
414  }
415 
417  template<typename... Ts> void fill_buffer(const Ts &...fields)
418  {
419  (..., append_to_buffer(fields));
420  }
421 
422  // C++20: constinit.
423  constexpr static std::string_view s_classname{"stream_to"};
424 };
425 
426 
427 template<typename Columns>
429  transaction_base &tx, std::string_view table_name, Columns const &columns) :
430  stream_to{tx, table_name, std::begin(columns), std::end(columns)}
431 {}
432 
433 
434 template<typename Iter>
436  transaction_base &tx, std::string_view table_name, Iter columns_begin,
437  Iter columns_end) :
438  stream_to{
439  tx,
440  tx.quote_name(
441  table_name,
442  separated_list(",", columns_begin, columns_end, [&tx](auto col) {
443  return tx.quote_name(*col);
444  }))}
445 {}
446 } // namespace pqxx
447 #endif
Interface definition (and common code) for "transaction" classes.
Definition: transaction_base.hxx:72
stream_to(transaction_base &tx, std::string_view table_name)
Create a stream, without specifying columns.
Definition: stream_to.hxx:168
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:116
stream_to & operator<<(Row const &row)
Insert a row of data.
Definition: stream_to.hxx:214
Reference to a field in a result set.
Definition: field.hxx:30
bool is_null(TYPE const &value) noexcept
Is value null?
Definition: strconv.hxx:364
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:100
Efficiently write data directly to a database table.
Definition: stream_to.hxx:76
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:375
Base class for things that monopolise a transaction&#39;s attention.
Definition: transaction_focus.hxx:24
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:39
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:234
connection & conn() const
The connection in which this transaction lives.
Definition: transaction_base.hxx:523
std::initializer_list< std::string_view > table_path
Representation of a PostgreSQL table path.
Definition: connection.hxx:119
std::string quote_columns(STRINGS const &columns) const
Quote and comma-separate a series of column names.
Definition: connection.hxx:1134
bool operator!() const noexcept
Has this stream been through its concluding complete()?
Definition: stream_to.hxx:193
std::string quote_name(std::string_view identifier) const
Escape an SQL identifier for use in a query.
Definition: transaction_base.hxx:216
Reference to one row in a result.
Definition: row.hxx:42
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:343
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:22
Stream data from the database.
Definition: stream_from.hxx:71
Traits class for use in string conversions.
Definition: strconv.hxx:152
void write_values(Ts const &...fields)
Insert values as a row.
Definition: stream_to.hxx:244
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:483