libpqxx
The C++ client library for PostgreSQL
params.hxx
Go to the documentation of this file.
1 /* Helpers for prepared statements and parameterised statements.
2  *
3  * See @ref connection and @ref transaction_base for more.
4  *
5  * Copyright (c) 2000-2026, Jeroen T. Vermeulen.
6  *
7  * See COPYING for copyright license. If you did not receive a file called
8  * COPYING with this source code, please notify the distributor of this
9  * mistake, or contact the author.
10  */
11 #ifndef PQXX_PARAMS_HXX
12 #define PQXX_PARAMS_HXX
13 
14 #if !defined(PQXX_HEADER_PRE)
15 # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
16 #endif
17 
18 #include <array>
19 #include <format>
20 
22 #include "pqxx/types.hxx"
23 
24 
25 namespace pqxx::internal
26 {
28 PQXX_PURE [[nodiscard]] inline constexpr pqxx::encoding_group
29 get_encoding_group(encoding_group const &enc, sl = sl::current()) noexcept
30 {
31  return enc;
32 }
33 
34 
36 
39 PQXX_PURE [[nodiscard]] inline constexpr pqxx::encoding_group
40 get_encoding_group(ctx c, sl = sl::current()) noexcept
41 {
42  return c.enc;
43 }
44 
45 
48 get_encoding_group(connection const &, sl = sl::current());
49 
50 
53 get_encoding_group(transaction_base const &, sl = sl::current());
54 } // namespace pqxx::internal
55 
56 
57 namespace pqxx
58 {
60 
65 {
66 public:
67  params() = default;
68 
69  // NOLINTBEGIN(google-explicit-constructor,hicpp-explicit-conversions)
70 
72 
82  template<typename First, typename... Args>
83  params(First &&first, Args &&...args)
84  {
85  sl loc;
86  if constexpr (std::is_same_v<std::remove_cvref<First>, conversion_context>)
87  loc = first.loc;
88  else
89  loc = sl::current();
90 
91  if constexpr (requires(encoding_group eg) {
93  })
94  {
95  // First argument is a source of an encoding group, not a parameter.
97  append_pack(loc, std::forward<Args>(args)...);
98  }
99  else
100  {
101  // The first argument is just a regular parameter. Append it first.
102  append_pack(
103  loc, std::forward<First>(first), std::forward<Args>(args)...);
104  }
105  }
106 
107  // NOLINTEND(google-explicit-constructor,hicpp-explicit-conversions)
108 
110 
116  void reserve(std::size_t n) &;
117 
119  [[nodiscard]] constexpr auto size() const noexcept
120  {
121  return m_params.size();
122  }
123 
125 
130  [[nodiscard]] constexpr auto ssize() const { return std::ssize(m_params); }
131 
133  void append(sl = sl::current()) &;
134 
136 
139  void append(zview, sl = sl::current()) &;
140 
142 
145  void append(std::string const &, sl = sl::current()) &;
146 
148  void append(std::string &&, sl = sl::current()) &;
149 
151 
154  void append(bytes_view, sl = sl::current()) &;
155 
157 
160  template<binary DATA> void append(DATA const &data, sl loc = sl::current()) &
161  {
162  append(binary_cast(data), loc);
163  }
164 
166  void append(bytes &&, sl = sl::current()) &;
167 
169  void append(params const &value, sl = sl::current()) &;
170 
172  void append(params &&value, sl = sl::current()) &;
173 
176  template<typename TYPE>
177  void append([[maybe_unused]] TYPE const &value, sl loc = sl::current()) &
178  {
179  // TODO: Pool storage for multiple string conversions in one buffer?
180  if constexpr (pqxx::always_null<TYPE>())
181  {
182  m_params.emplace_back();
183  }
184  else if (is_null(value))
185  {
186  m_params.emplace_back();
187  }
188  else
189  {
190  // TODO: Block-allocate storage for parameters.
191  m_params.emplace_back(
192  entry{to_string(value, conversion_context{m_enc, loc})});
193  }
194  }
195 
197  template<std::ranges::range RANGE>
198  void append_multi(RANGE const &range, sl loc = sl::current()) &
199  {
200  if constexpr (std::ranges::sized_range<RANGE>)
201  reserve(std::size(*this) + std::size(range));
202  for (auto &value : range) append(value, loc);
203  }
204 
206 
215  [[nodiscard]] pqxx::internal::c_params make_c_params(sl loc) const;
216 
217 private:
219  template<typename... Args> void append_pack(sl loc, Args &&...args)
220  {
221  reserve(size() + sizeof...(args));
222  ((this->append(std::forward<Args>(args), loc)), ...);
223  }
224 
226 
228  void append_pack(sl) const noexcept {}
229 
230  // The way we store a parameter depends on whether it's binary or text
231  // (most types are text), and whether we're responsible for storing the
232  // contents.
233  using entry =
234  std::variant<std::nullptr_t, zview, std::string, bytes_view, bytes>;
235  std::vector<entry> m_params;
236 
238 
239  static constexpr std::string_view s_overflow{
240  "Statement parameter length overflow."sv};
241 };
242 
243 
245 
255 template<typename COUNTER = unsigned int> class placeholders final
256 {
257 public:
259  static inline constexpr unsigned int max_params{
260  (std::numeric_limits<COUNTER>::max)()};
261 
263  {
264  static constexpr auto initial{"$1\0"sv};
265  initial.copy(std::data(m_buf), std::size(initial));
266  }
267 
269 
272  [[nodiscard]] constexpr zview view() const & noexcept
273  {
274  return zview{std::data(m_buf), m_len};
275  }
276 
278 
283  [[nodiscard]] std::string get() const
284  {
285  return std::string(std::data(m_buf), m_len);
286  }
287 
289  void next(sl loc = sl::current()) &
290  {
291  conversion_context const c{{}, loc};
292  if (m_current >= max_params)
293  throw range_error{
294  std::format(
295  "Too many parameters in one statement: limit is {}.", max_params),
296  loc};
297  PQXX_ASSUME(m_current > 0);
298  ++m_current;
299  if (m_current % 10 == 0)
300  {
301  // Carry the 1. Don't get too clever for this relatively rare
302  // case, just rewrite the entire number. Leave the $ in place
303  // though.
304  char *const data{std::data(m_buf)};
305 
306  auto const written{pqxx::into_buf<COUNTER>(
307  {data + 1, data + std::size(m_buf) - 1}, m_current, c)};
308  std::size_t const end{1 + written};
309  assert(end < std::size(m_buf));
310  data[end] = '\0';
311  m_len = check_cast<COUNTER>(end, "placeholders counter", loc);
312  }
313  else [[likely]]
314  {
315  // Shortcut for the common case: just increment that last digit.
316  ++m_buf.at(m_len - 1);
317  }
318  }
319 
321  [[nodiscard]] COUNTER count() const noexcept { return m_current; }
322 
323 private:
325  COUNTER m_current = 1;
326 
328  COUNTER m_len = 2;
329 
331 
338  std::array<char, std::numeric_limits<COUNTER>::digits10 + 3> m_buf;
339 };
340 } // namespace pqxx
341 #endif
Connection to a database.
Definition: connection.hxx:273
Build a parameter list for a parameterised or prepared statement.
Definition: params.hxx:65
constexpr auto size() const noexcept
Get the number of parameters currently in this params.
Definition: params.hxx:119
params(First &&first, Args &&...args)
Pre-populate a params with args. Feel free to add more later.
Definition: params.hxx:83
constexpr auto ssize() const
Get the number of parameters (signed).
Definition: params.hxx:130
void append([[maybe_unused]] TYPE const &value, sl loc=sl::current()) &
Definition: params.hxx:177
void append(DATA const &data, sl loc=sl::current()) &
Append a non-null binary parameter.
Definition: params.hxx:160
params()=default
void append_multi(RANGE const &range, sl loc=sl::current()) &
Append all elements of range as parameters.
Definition: params.hxx:198
Generate parameter placeholders for use in an SQL statement.
Definition: params.hxx:256
static constexpr unsigned int max_params
Maximum number of parameters we support.
Definition: params.hxx:259
COUNTER count() const noexcept
Return the current placeholder number. The initial placeholder is 1.
Definition: params.hxx:321
std::string get() const
Read the current placeholder text, as a std::string.
Definition: params.hxx:283
void next(sl loc=sl::current()) &
Move on to the next parameter.
Definition: params.hxx:289
placeholders()
Definition: params.hxx:262
constexpr zview view() const &noexcept
Read an ephemeral version of the current placeholder text.
Definition: params.hxx:272
A C++ equivalent to PostgreSQL's range types.
Definition: range.hxx:268
Marker-type wrapper: zero-terminated std::string_view.
Definition: zview.hxx:55
Something is out of range, similar to std::out_of_range.
Definition: except.hxx:651
Interface definition (and common code) for "transaction" classes.
Definition: transaction_base.hxx:151
#define PQXX_LIBEXPORT
Definition: header-pre.hxx:206
#define PQXX_PURE
Definition: header-pre.hxx:64
#define PQXX_ASSUME(condition)
Definition: header-pre.hxx:228
Private namespace for libpqxx's internal use; do not access.
Definition: connection.cxx:333
constexpr PQXX_PURE pqxx::encoding_group get_encoding_group(encoding_group const &enc, sl=sl::current()) noexcept
Identity function for encoding_group, for regularity.
Definition: params.hxx:29
The home of all libpqxx classes, functions, templates, etc.
Definition: array.cxx:26
std::span< std::byte const > bytes_view
Type alias for a view of bytes.
Definition: types.hxx:188
std::source_location sl
Convenience alias for std::source_location. It's just too long.
Definition: types.hxx:38
PQXX_LIBEXPORT std::string to_string(field_ref const &value, ctx)
Convert a field_ref to a string.
Definition: field.hxx:891
bytes_view binary_cast(TYPE const &data)
Cast binary data to a type that libpqxx will recognise as binary.
Definition: util.hxx:260
encoding_group
Definition: encoding_group.hxx:40
@ unknown
Default: indeterminate encoding. All we know is it supports ASCII.
constexpr bool is_null(TYPE const &value) noexcept
Is value a null?
Definition: strconv.hxx:764
requires(pqxx::internal::to_buf_7< TYPE > or pqxx::internal::to_buf_8< TYPE >) const eval bool supports_to_buf_8()
Is the libpqxx 8 version of to_buf() supported for TYPE?
Definition: strconv.hxx:417
conversion_context const & ctx
Convenience alias: const reference to a pqxx::conversion_context.
Definition: strconv.hxx:201
std::vector< std::byte > bytes
Type alias for a container containing bytes.
Definition: util.hxx:240
format
Format code: is data text or binary?
Definition: types.hxx:121
Contextual parameters for string conversions implementations.
Definition: strconv.hxx:163
Internal type: encode statement parameters.
Definition: statement_parameters.hxx:44