libpqxx  7.6.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-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 
79 class PQXX_LIBEXPORT stream_to : transaction_focus
80 {
81 public:
83 
104  transaction_base &tx, std::string_view path, std::string_view columns = "")
105  {
106  return stream_to{tx, path, columns};
107  }
108 
110 
119  static stream_to table(
120  transaction_base &tx, table_path path,
121  std::initializer_list<std::string_view> columns = {})
122  {
123  auto const &conn{tx.conn()};
124  return raw_table(tx, conn.quote_table(path), conn.quote_columns(columns));
125  }
126 
127 #if defined(PQXX_HAVE_CONCEPTS)
129 
136  template<PQXX_CHAR_STRINGS_ARG COLUMNS>
137  static stream_to
138  table(transaction_base &tx, table_path path, COLUMNS const &columns)
139  {
140  auto const &conn{tx.conn()};
141  return stream_to::raw_table(
142  tx, conn.quote_table(path), tx.conn().quote_columns(columns));
143  }
144 
146 
153  template<PQXX_CHAR_STRINGS_ARG COLUMNS>
154  static stream_to
155  table(transaction_base &tx, std::string_view path, COLUMNS const &columns)
156  {
157  return stream_to::raw_table(tx, path, tx.conn().quote_columns(columns));
158  }
159 #endif // PQXX_HAVE_CONCEPTS
160 
162 
171  [[deprecated("Use table() or raw_table() factory.")]] stream_to(
172  transaction_base &tx, std::string_view table_name) :
173  stream_to{tx, table_name, ""sv}
174  {}
175 
177 
179  template<typename Columns>
180  [[deprecated("Use table() or raw_table() factory.")]] stream_to(
181  transaction_base &, std::string_view table_name, Columns const &columns);
182 
184 
186  template<typename Iter>
187  [[deprecated("Use table() or raw_table() factory.")]] stream_to(
188  transaction_base &, std::string_view table_name, Iter columns_begin,
189  Iter columns_end);
190 
191  ~stream_to() noexcept;
192 
194  [[nodiscard]] operator bool() const noexcept { return not m_finished; }
196  [[nodiscard]] bool operator!() const noexcept { return m_finished; }
197 
199 
205  void complete();
206 
208 
217  template<typename Row> stream_to &operator<<(Row const &row)
218  {
219  write_row(row);
220  return *this;
221  }
222 
224 
229 
231 
237  template<typename Row> void write_row(Row const &row)
238  {
239  fill_buffer(row);
240  write_buffer();
241  }
242 
244 
247  template<typename... Ts> void write_values(Ts const &...fields)
248  {
249  fill_buffer(fields...);
250  write_buffer();
251  }
252 
253 private:
255  stream_to(
256  transaction_base &tx, std::string_view path, std::string_view columns);
257 
258  bool m_finished = false;
259 
261  std::string m_buffer;
262 
264  std::string m_field_buf;
265 
267  void write_raw_line(std::string_view);
268 
270 
272  void write_buffer();
273 
275  static constexpr std::string_view null_field{"\\N\t"};
276 
278  template<typename T>
279  static std::enable_if_t<nullness<T>::always_null, std::size_t>
280  estimate_buffer(T const &)
281  {
282  return std::size(null_field);
283  }
284 
286 
289  template<typename T>
290  static std::enable_if_t<not nullness<T>::always_null, std::size_t>
291  estimate_buffer(T const &field)
292  {
293  return is_null(field) ? std::size(null_field) : size_buffer(field);
294  }
295 
297  void escape_field_to_buffer(std::string_view data);
298 
300 
306  template<typename Field>
307  std::enable_if_t<not nullness<Field>::always_null>
308  append_to_buffer(Field const &f)
309  {
310  // We append each field, terminated by a tab. That will leave us with
311  // one tab too many, assuming we write any fields at all; we remove that
312  // at the end.
313  if (is_null(f))
314  {
315  // Easy. Append null and tab in one go.
316  m_buffer.append(null_field);
317  }
318  else
319  {
320  // Convert f into m_buffer.
321 
322  using traits = string_traits<Field>;
323  auto const budget{estimate_buffer(f)};
324  auto const offset{std::size(m_buffer)};
325 
326  if constexpr (std::is_arithmetic_v<Field>)
327  {
328  // Specially optimised for "safe" types, which never need any
329  // escaping. Convert straight into m_buffer.
330 
331  // The budget we get from size_buffer() includes room for the trailing
332  // zero, which we must remove. But we're also inserting tabs between
333  // fields, so we re-purpose the extra byte for that.
334  auto const total{offset + budget};
335  m_buffer.resize(total);
336  auto const data{m_buffer.data()};
337  char *const end{traits::into_buf(data + offset, data + total, f)};
338  *(end - 1) = '\t';
339  // Shrink to fit. Keep the tab though.
340  m_buffer.resize(static_cast<std::size_t>(end - data));
341  }
342  else if constexpr (
343  std::is_same_v<Field, std::string> or
344  std::is_same_v<Field, std::string_view> or
345  std::is_same_v<Field, zview>)
346  {
347  // This string may need escaping.
348  m_field_buf.resize(budget);
349  escape_field_to_buffer(f);
350  }
351  else
352  {
353  // This field needs to be converted to a string, and after that,
354  // escaped as well.
355  m_field_buf.resize(budget);
356  auto const data{m_field_buf.data()};
357  escape_field_to_buffer(
358  traits::to_buf(data, data + std::size(m_field_buf), f));
359  }
360  }
361  }
362 
364 
370  template<typename Field>
371  std::enable_if_t<nullness<Field>::always_null>
372  append_to_buffer(Field const &)
373  {
374  m_buffer.append(null_field);
375  }
376 
378  template<typename Container>
379  std::enable_if_t<not std::is_same_v<typename Container::value_type, char>>
380  fill_buffer(Container const &c)
381  {
382  // To avoid unnecessary allocations and deallocations, we run through c
383  // twice: once to determine how much buffer space we may need, and once to
384  // actually write it into the buffer.
385  std::size_t budget{0};
386  for (auto const &f : c) budget += estimate_buffer(f);
387  m_buffer.reserve(budget);
388  for (auto const &f : c) append_to_buffer(f);
389  }
390 
392  template<typename Tuple, std::size_t... indexes>
393  static std::size_t
394  budget_tuple(Tuple const &t, std::index_sequence<indexes...>)
395  {
396  return (estimate_buffer(std::get<indexes>(t)) + ...);
397  }
398 
400  template<typename Tuple, std::size_t... indexes>
401  void append_tuple(Tuple const &t, std::index_sequence<indexes...>)
402  {
403  (append_to_buffer(std::get<indexes>(t)), ...);
404  }
405 
407  template<typename... Elts> void fill_buffer(std::tuple<Elts...> const &t)
408  {
409  using indexes = std::make_index_sequence<sizeof...(Elts)>;
410 
411  m_buffer.reserve(budget_tuple(t, indexes{}));
412  append_tuple(t, indexes{});
413  }
414 
416  template<typename... Ts> void fill_buffer(const Ts &...fields)
417  {
418  (..., append_to_buffer(fields));
419  }
420 
421  constexpr static std::string_view s_classname{"stream_to"};
422 };
423 
424 
425 template<typename Columns>
427  transaction_base &tx, std::string_view table_name, Columns const &columns) :
428  stream_to{tx, table_name, std::begin(columns), std::end(columns)}
429 {}
430 
431 
432 template<typename Iter>
434  transaction_base &tx, std::string_view table_name, Iter columns_begin,
435  Iter columns_end) :
436  stream_to{
437  tx,
438  tx.quote_name(
439  table_name,
440  separated_list(",", columns_begin, columns_end, [&tx](auto col) {
441  return tx.quote_name(*col);
442  }))}
443 {}
444 } // namespace pqxx
445 
446 #include "pqxx/internal/compiler-internal-post.hxx"
447 #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:42
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:368
std::initializer_list< std::string_view > table_path
Representation of a PostgreSQL table path.
Definition: connection.hxx:119
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:479
bool is_null(TYPE const &value) noexcept
Is value null?
Definition: strconv.hxx:357
Reference to one row in a result.
Definition: row.hxx:46
Stream data from the database.
Definition: stream_from.hxx:73
Efficiently write data directly to a database table.
Definition: stream_to.hxx:80
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:103
stream_to & operator<<(Row const &row)
Insert a row of data.
Definition: stream_to.hxx:217
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:119
void write_values(Ts const &...fields)
Insert values as a row.
Definition: stream_to.hxx:247
stream_to(transaction_base &tx, std::string_view table_name)
Create a stream, without specifying columns.
Definition: stream_to.hxx:171
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:237
bool operator!() const noexcept
Has this stream been through its concluding complete()?
Definition: stream_to.hxx:196
Interface definition (and common code) for "transaction" classes.
Definition: transaction_base.hxx:76
connection & conn() const
The connection in which this transaction lives.
Definition: transaction_base.hxx:524
std::string quote_name(std::string_view identifier) const
Escape an SQL identifier for use in a query.
Definition: transaction_base.hxx:218
Base class for things that monopolise a transaction's attention.
Definition: transaction_focus.hxx:28