libpqxx  7.4.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/transaction_base.hxx"
20 
21 
22 namespace pqxx
23 {
25 
78 class PQXX_LIBEXPORT stream_to : transaction_focus
79 {
80 public:
81  // TODO: Support WHERE clause?
82 
84 
105  transaction_base &tx, std::string_view path, std::string_view columns = "")
106  {
107  return stream_to{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 
129 
138  PQXX_DEPRECATED("Use table() or raw_table() factory.")
139  stream_to(transaction_base &tx, std::string_view table_name) :
140  stream_to{tx, table_name, ""sv}
141  {}
142 
144 
146  template<typename Columns>
147  PQXX_DEPRECATED("Use table() or raw_table() factory.")
148  stream_to(
149  transaction_base &, std::string_view table_name, Columns const &columns);
150 
152 
154  template<typename Iter>
155  PQXX_DEPRECATED("Use table() or raw_table() factory.")
156  stream_to(
157  transaction_base &, std::string_view table_name, Iter columns_begin,
158  Iter columns_end);
159 
160  ~stream_to() noexcept;
161 
163  [[nodiscard]] operator bool() const noexcept { return not m_finished; }
165  [[nodiscard]] bool operator!() const noexcept { return m_finished; }
166 
168 
174  void complete();
175 
177 
186  template<typename Row> stream_to &operator<<(Row const &row)
187  {
188  write_row(row);
189  return *this;
190  }
191 
193 
198 
200 
206  template<typename Row> void write_row(Row const &row)
207  {
208  fill_buffer(row);
209  write_buffer();
210  }
211 
213 
216  template<typename... Ts> void write_values(Ts const &...fields)
217  {
218  fill_buffer(fields...);
219  write_buffer();
220  }
221 
222 private:
224  stream_to(
225  transaction_base &tx, std::string_view path, std::string_view columns);
226 
227  bool m_finished = false;
228 
230  std::string m_buffer;
231 
233  std::string m_field_buf;
234 
236  void write_raw_line(std::string_view);
237 
239 
241  void write_buffer();
242 
244  static constexpr std::string_view null_field{"\\N\t"};
245 
247  template<typename T>
248  static std::enable_if_t<nullness<T>::always_null, std::size_t>
249  estimate_buffer(T const &)
250  {
251  return std::size(null_field);
252  }
253 
255 
258  template<typename T>
259  static std::enable_if_t<not nullness<T>::always_null, std::size_t>
260  estimate_buffer(T const &field)
261  {
262  return is_null(field) ? std::size(null_field) : size_buffer(field);
263  }
264 
266  void escape_field_to_buffer(std::string_view);
267 
269 
275  template<typename Field>
276  std::enable_if_t<not nullness<Field>::always_null>
277  append_to_buffer(Field const &f)
278  {
279  // We append each field, terminated by a tab. That will leave us with
280  // one tab too many, assuming we write any fields at all; we remove that
281  // at the end.
282  if (is_null(f))
283  {
284  // Easy. Append null and tab in one go.
285  m_buffer.append(null_field);
286  }
287  else
288  {
289  // Convert f into m_buffer.
290 
291  using traits = string_traits<Field>;
292  auto const budget{estimate_buffer(f)};
293  auto const offset{std::size(m_buffer)};
294 
295  if constexpr (std::is_arithmetic_v<Field>)
296  {
297  // Specially optimised for "safe" types, which never need any
298  // escaping. Convert straight into m_buffer.
299 
300  // The budget we get from size_buffer() includes room for the trailing
301  // zero, which we must remove. But we're also inserting tabs between
302  // fields, so we re-purpose the extra byte for that.
303  auto const total{offset + budget};
304  m_buffer.resize(total);
305  char *const end{traits::into_buf(
306  m_buffer.data() + offset, m_buffer.data() + total, f)};
307  *(end - 1) = '\t';
308  // Shrink to fit. Keep the tab though.
309  m_buffer.resize(static_cast<std::size_t>(end - m_buffer.data()));
310  }
311  else
312  {
313  // TODO: Specialise string/string_view/zview to skip to_buf()!
314  // This field may need escaping. First convert the value into
315  // m_field_buffer, then escape into its final place.
316  m_field_buf.resize(budget);
317  escape_field_to_buffer(traits::to_buf(
318  m_field_buf.data(), m_field_buf.data() + std::size(m_field_buf), f));
319  }
320  }
321  }
322 
324 
330  template<typename Field>
331  std::enable_if_t<nullness<Field>::always_null>
332  append_to_buffer(Field const &)
333  {
334  m_buffer.append(null_field);
335  }
336 
338  template<typename Container>
339  std::enable_if_t<not std::is_same_v<typename Container::value_type, char>>
340  fill_buffer(Container const &c)
341  {
342  // To avoid unnecessary allocations and deallocations, we run through c
343  // twice: once to determine how much buffer space we may need, and once to
344  // actually write it into the buffer.
345  std::size_t budget{0};
346  for (auto const &f : c) budget += estimate_buffer(f);
347  m_buffer.reserve(budget);
348  for (auto const &f : c) append_to_buffer(f);
349  }
350 
352  template<typename Tuple, std::size_t... indexes>
353  static std::size_t
354  budget_tuple(Tuple const &t, std::index_sequence<indexes...>)
355  {
356  return (estimate_buffer(std::get<indexes>(t)) + ...);
357  }
358 
360  template<typename Tuple, std::size_t... indexes>
361  void append_tuple(Tuple const &t, std::index_sequence<indexes...>)
362  {
363  (append_to_buffer(std::get<indexes>(t)), ...);
364  }
365 
367  template<typename... Elts> void fill_buffer(std::tuple<Elts...> const &t)
368  {
369  using indexes = std::make_index_sequence<sizeof...(Elts)>;
370 
371  m_buffer.reserve(budget_tuple(t, indexes{}));
372  append_tuple(t, indexes{});
373  }
374 
376  template<typename... Ts> void fill_buffer(const Ts &...fields)
377  {
378  (..., append_to_buffer(fields));
379  }
380 
381  constexpr static std::string_view s_classname{"stream_to"};
382 };
383 
384 
385 template<typename Columns>
386 inline stream_to::stream_to(
387  transaction_base &tx, std::string_view table_name, Columns const &columns) :
388  stream_to{tx, table_name, std::begin(columns), std::end(columns)}
389 {}
390 
391 
392 template<typename Iter>
393 inline stream_to::stream_to(
394  transaction_base &tx, std::string_view table_name, Iter columns_begin,
395  Iter columns_end) :
396  stream_to{
397  tx,
398  tx.quote_name(
399  table_name,
400  separated_list(",", columns_begin, columns_end, [&tx](auto col) {
401  return tx.quote_name(*col);
402  }))}
403 {}
404 } // namespace pqxx
405 
406 #include "pqxx/internal/compiler-internal-post.hxx"
407 #endif
bool operator!() const noexcept
Has this stream been through its concluding complete()?
Definition: stream_to.hxx:165
Reference to one row in a result.
Definition: row.hxx:45
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
Traits class for use in string conversions.
Definition: strconv.hxx:148
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:25
bool is_null(TYPE const &value) noexcept
Is value null?
Definition: strconv.hxx:353
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::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:332
void write_values(Ts const &...fields)
Insert values as a row.
Definition: stream_to.hxx:216
connection & conn() const
The connection in which this transaction lives.
Definition: transaction_base.hxx:481
stream_to & operator<<(Row const &row)
Insert a row of data.
Definition: stream_to.hxx:186
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:206
Efficiently write data directly to a database table.
Definition: stream_to.hxx:78
Reference to a field in a result set.
Definition: field.hxx:33
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
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::string quote_name(std::string_view identifier) const
Escape an SQL identifier for use in a query.
Definition: transaction_base.hxx:188
Base class for things that monopolise a transaction&#39;s attention.
Definition: transaction_focus.hxx:27
Stream data from the database.
Definition: stream_from.hxx:69
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
Interface definition (and common code) for "transaction" classes.
Definition: transaction_base.hxx:74
std::initializer_list< std::string_view > table_path
Representation of a PostgreSQL table path.
Definition: connection.hxx:120