libpqxx  7.1.2
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-2020, 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 
69 class PQXX_LIBEXPORT stream_to : internal::transactionfocus
70 {
71 public:
73 
80  stream_to(transaction_base &, std::string_view table_name);
81 
83  template<typename Columns>
84  stream_to(
85  transaction_base &, std::string_view table_name, Columns const &columns);
86 
88  template<typename Iter>
89  stream_to(
90  transaction_base &, std::string_view table_name, Iter columns_begin,
91  Iter columns_end);
92 
93  ~stream_to() noexcept;
94 
95  [[nodiscard]] operator bool() const noexcept { return not m_finished; }
96  [[nodiscard]] bool operator!() const noexcept { return m_finished; }
97 
99 
105  void complete();
106 
108 
117  template<typename Row> stream_to &operator<<(Row const &row)
118  {
119  write_row(row);
120  return *this;
121  }
122 
124 
129 
131 
137  template<typename Row> void write_row(Row const &row)
138  {
139  fill_buffer(row);
140  write_buffer();
141  }
142 
144 
147  template<typename... Ts> void write_values(const Ts &... fields)
148  {
149  fill_buffer(fields...);
150  write_buffer();
151  }
152 
153 private:
154  bool m_finished = false;
155 
157  std::string m_buffer;
158 
160  std::string m_field_buf;
161 
163  void write_raw_line(std::string_view);
164 
166 
168  void write_buffer();
169 
171  static constexpr std::string_view null_field{"\\N\t"};
172 
174  template<typename T>
175  static std::enable_if_t<nullness<T>::always_null, std::size_t>
176  estimate_buffer(T const &)
177  {
178  return null_field.size();
179  }
180 
182 
185  template<typename T>
186  static std::enable_if_t<not nullness<T>::always_null, std::size_t>
187  estimate_buffer(T const &field)
188  {
189  return is_null(field) ? null_field.size() :
191  }
192 
194  void escape_field_to_buffer(std::string_view);
195 
197 
203  template<typename Field>
204  std::enable_if_t<not nullness<Field>::always_null>
205  append_to_buffer(Field const &f)
206  {
207  // We append each field, terminated by a tab. That will leave us with
208  // one tab too many, assuming we write any fields at all; we remove that
209  // at the end.
210  if (is_null(f))
211  {
212  // Easy. Append null and tab in one go.
213  m_buffer.append(null_field);
214  }
215  else
216  {
217  // Convert f into m_buffer.
218 
219  using traits = string_traits<Field>;
220  auto const budget{estimate_buffer(f)};
221  auto const offset{m_buffer.size()};
222 
223  if constexpr (std::is_arithmetic_v<Field>)
224  {
225  // Specially optimised for "safe" types, which never need any
226  // escaping. Convert straight into m_buffer.
227 
228  // The budget we get from size_buffer() includes room for the trailing
229  // zero, which we must remove. But we're also inserting tabs between
230  // fields, so we re-purpose the extra byte for that.
231  auto const total{offset + budget};
232  m_buffer.resize(total);
233  char *const end{traits::into_buf(
234  m_buffer.data() + offset, m_buffer.data() + total, f)};
235  *(end - 1) = '\t';
236  // Shrink to fit. Keep the tab though.
237  m_buffer.resize(static_cast<std::size_t>(end - m_buffer.data()));
238  }
239  else
240  {
241  // TODO: Specialise string/string_view/zview to skip to_buf()!
242  // This field may need escaping. First convert the value into
243  // m_field_buffer, then escape into its final place.
244  m_field_buf.resize(budget);
245  escape_field_to_buffer(traits::to_buf(
246  m_field_buf.data(), m_field_buf.data() + m_field_buf.size(), f));
247  }
248  }
249  }
250 
252 
258  template<typename Field>
259  std::enable_if_t<nullness<Field>::always_null>
260  append_to_buffer(Field const &)
261  {
262  m_buffer.append(null_field);
263  }
264 
266  template<typename Container> void fill_buffer(Container const &c)
267  {
268  // To avoid unnecessary allocations and deallocations, we run through c
269  // twice: once to determine how much buffer space we may need, and once to
270  // actually write it into the buffer.
271  std::size_t budget{0};
272  for (auto const &f : c) budget += estimate_buffer(f);
273  m_buffer.reserve(budget);
274  for (auto const &f : c) append_to_buffer(f);
275  }
276 
278  template<typename Tuple, std::size_t... indexes>
279  static std::size_t
280  budget_tuple(Tuple const &t, std::index_sequence<indexes...>)
281  {
282  return (estimate_buffer(std::get<indexes>(t)) + ...);
283  }
284 
286  template<typename Tuple, std::size_t... indexes>
287  void append_tuple(Tuple const &t, std::index_sequence<indexes...>)
288  {
289  (append_to_buffer(std::get<indexes>(t)), ...);
290  }
291 
293  template<typename... Elts> void fill_buffer(std::tuple<Elts...> const &t)
294  {
295  using indexes = std::make_index_sequence<sizeof...(Elts)>;
296 
297  m_buffer.reserve(budget_tuple(t, indexes{}));
298  append_tuple(t, indexes{});
299  }
300 
301  void set_up(transaction_base &, std::string_view table_name);
302  void set_up(
303  transaction_base &, std::string_view table_name,
304  std::string const &columns);
305 
307  template<typename... Ts> void fill_buffer(const Ts &... fields)
308  {
309  (..., append_to_buffer(fields));
310  }
311 };
312 
313 
314 template<typename Columns>
316  transaction_base &tb, std::string_view table_name, Columns const &columns) :
317  stream_to{tb, table_name, std::begin(columns), std::end(columns)}
318 {}
319 
320 
321 template<typename Iter>
323  transaction_base &tb, std::string_view table_name, Iter columns_begin,
324  Iter columns_end) :
325  namedclass{"stream_to", table_name}, internal::transactionfocus{tb}
326 {
327  set_up(tb, table_name, separated_list(",", columns_begin, columns_end));
328 }
329 } // namespace pqxx
330 
331 #include "pqxx/internal/compiler-internal-post.hxx"
332 #endif
Reference to one row in a result.
Definition: row.hxx:43
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:137
Traits class for use in string conversions.
Definition: strconv.hxx:123
stream_to & operator<<(Row const &row)
Insert a row of data.
Definition: stream_to.hxx:117
stream_to(transaction_base &, std::string_view table_name)
Create a stream, without specifying columns.
Definition: stream_to.cxx:45
static std::size_t size_buffer(TYPE const &value) noexcept
Estimate how much buffer space is needed to represent value.
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:307
Efficiently write data directly to a database table.
Definition: stream_to.hxx:69
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
Stream data from the database.
Definition: stream_from.hxx:60
bool operator!() const noexcept
Definition: stream_to.hxx:96
Interface definition (and common code) for "transaction" classes.
Definition: transaction_base.hxx:67
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:349
Reference to a field in a result set.
Definition: field.hxx:32
void write_values(const Ts &... fields)
Insert values as a row.
Definition: stream_to.hxx:147