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
20namespace pqxx
21{
23
76class PQXX_LIBEXPORT stream_to : transaction_focus
77{
78public:
80
101 transaction_base &tx, std::string_view path, std::string_view columns = "")
102 {
103 return {tx, path, columns};
104 }
105
107
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)
126
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
250private:
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
427template<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
434template<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
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:23
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
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
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
std::initializer_list< std::string_view > table_path
Representation of a PostgreSQL table path.
Definition: connection.hxx:119
bool is_null(TYPE const &value) noexcept
Is value null?
Definition: strconv.hxx:364
Reference to one row in a result.
Definition: row.hxx:43
Stream data from the database.
Definition: stream_from.hxx:72
Efficiently write data directly to a database table.
Definition: stream_to.hxx:77
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
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
void write_values(Ts const &...fields)
Insert values as a row.
Definition: stream_to.hxx:244
stream_to(transaction_base &tx, std::string_view table_name)
Create a stream, without specifying columns.
Definition: stream_to.hxx:168
stream_to & operator<<(Row const &row)
Insert a row of data.
Definition: stream_to.hxx:214
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
bool operator!() const noexcept
Has this stream been through its concluding complete()?
Definition: stream_to.hxx:193
Interface definition (and common code) for "transaction" classes.
Definition: transaction_base.hxx:73
connection & conn() const
The connection in which this transaction lives.
Definition: transaction_base.hxx:523
std::string quote_name(std::string_view identifier) const
Escape an SQL identifier for use in a query.
Definition: transaction_base.hxx:216
Base class for things that monopolise a transaction's attention.
Definition: transaction_focus.hxx:25