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_t<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(to_string(value, conversion_context{m_enc, loc}));
192  }
193  }
194 
196  template<std::ranges::range RANGE>
197  void append_multi(RANGE const &range, sl loc = sl::current()) &
198  {
199  if constexpr (std::ranges::sized_range<RANGE>)
200  reserve(std::size(*this) + std::size(range));
201  for (auto &value : range) append(value, loc);
202  }
203 
205 
214  [[nodiscard]] pqxx::internal::c_params make_c_params(sl loc) const;
215 
216 private:
218  template<typename... Args> void append_pack(sl loc, Args &&...args)
219  {
220  reserve(size() + sizeof...(args));
221  ((this->append(std::forward<Args>(args), loc)), ...);
222  }
223 
225 
227  void append_pack(sl) const noexcept {}
228 
229  // The way we store a parameter depends on whether it's binary or text
230  // (most types are text), and whether we're responsible for storing the
231  // contents.
232  using entry =
233  std::variant<std::nullptr_t, zview, std::string, bytes_view, bytes>;
234  std::vector<entry> m_params;
235 
237 
238  static constexpr std::string_view s_overflow{
239  "Statement parameter length overflow."sv};
240 };
241 
242 
244 
254 template<typename COUNTER = unsigned int> class placeholders final
255 {
256 public:
258  static inline constexpr unsigned int max_params{
259  (std::numeric_limits<COUNTER>::max)()};
260 
262  {
263  static constexpr auto initial{"$1\0"sv};
264  initial.copy(std::data(m_buf), std::size(initial));
265  }
266 
268 
271  [[nodiscard]] constexpr zview view() const & noexcept
272  {
273  return zview{std::data(m_buf), m_len};
274  }
275 
277 
282  [[nodiscard]] std::string get() const
283  {
284  return std::string(std::data(m_buf), m_len);
285  }
286 
288  void next(sl loc = sl::current()) &
289  {
290  conversion_context const c{{}, loc};
291  if (m_current >= max_params)
292  throw range_error{
293  std::format(
294  "Too many parameters in one statement: limit is {}.", max_params),
295  loc};
296  PQXX_ASSUME(m_current > 0);
297  ++m_current;
298  if (m_current % 10 == 0)
299  {
300  // Carry the 1. Don't get too clever for this relatively rare
301  // case, just rewrite the entire number. Leave the $ in place
302  // though.
303  char *const data{std::data(m_buf)};
304 
305  auto const written{pqxx::into_buf<COUNTER>(
306  {data + 1, data + std::size(m_buf) - 1}, m_current, c)};
307  std::size_t const end{1 + written};
308  assert(end < std::size(m_buf));
309  data[end] = '\0';
310  m_len = check_cast<COUNTER>(end, "placeholders counter", loc);
311  }
312  else [[likely]]
313  {
314  // Shortcut for the common case: just increment that last digit.
315  ++m_buf.at(m_len - 1);
316  }
317  }
318 
320  [[nodiscard]] COUNTER count() const noexcept { return m_current; }
321 
322 private:
324  COUNTER m_current = 1;
325 
327  COUNTER m_len = 2;
328 
330 
337  std::array<char, std::numeric_limits<COUNTER>::digits10 + 3> m_buf;
338 };
339 } // namespace pqxx
340 #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:197
Generate parameter placeholders for use in an SQL statement.
Definition: params.hxx:255
static constexpr unsigned int max_params
Maximum number of parameters we support.
Definition: params.hxx:258
COUNTER count() const noexcept
Return the current placeholder number. The initial placeholder is 1.
Definition: params.hxx:320
std::string get() const
Read the current placeholder text, as a std::string.
Definition: params.hxx:282
void next(sl loc=sl::current()) &
Move on to the next parameter.
Definition: params.hxx:288
placeholders()
Definition: params.hxx:261
constexpr zview view() const &noexcept
Read an ephemeral version of the current placeholder text.
Definition: params.hxx:271
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:225
#define PQXX_PURE
Definition: header-pre.hxx:64
#define PQXX_ASSUME(condition)
Definition: header-pre.hxx:247
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