libpqxx
The C++ client library for PostgreSQL
Loading...
Searching...
No Matches
connection.hxx
Go to the documentation of this file.
1/* Definition of the connection class.
2 *
3 * pqxx::connection encapsulates a connection to a database.
4 *
5 * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/connection instead.
6 *
7 * Copyright (c) 2000-2025, 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_CONNECTION
14#define PQXX_H_CONNECTION
15
16#if !defined(PQXX_HEADER_PRE)
17# error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
18#endif
19
20#include <cstddef>
21#include <ctime>
22#include <functional>
23#include <initializer_list>
24#include <list>
25#include <map>
26#include <memory>
27#include <optional>
28#include <string_view>
29#include <tuple>
30#include <utility>
31
32// Double-check in order to suppress an overzealous Visual C++ warning (#418).
33#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_RANGES)
34# include <ranges>
35#endif
36
37#include "pqxx/errorhandler.hxx"
38#include "pqxx/except.hxx"
40#include "pqxx/params.hxx"
42#include "pqxx/strconv.hxx"
43#include "pqxx/types.hxx"
44#include "pqxx/util.hxx"
45#include "pqxx/zview.hxx"
46
47
79
80namespace pqxx::internal
81{
82class sql_cursor;
83
84#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_RANGES)
86template<typename T>
87concept ZKey_ZValues = std::ranges::input_range<T> and requires(T t) {
88 { std::cbegin(t) };
89 { std::get<0>(*std::cbegin(t)) } -> ZString;
90 { std::get<1>(*std::cbegin(t)) } -> ZString;
91} and std::tuple_size_v<typename std::ranges::iterator_t<T>::value_type> == 2;
92#endif // PQXX_HAVE_CONCEPTS
93
94
96
103void PQXX_COLD PQXX_LIBEXPORT skip_init_ssl(int skips) noexcept;
104} // namespace pqxx::internal
105
106
108{
109class connection_dbtransaction;
112class connection_notification_receiver;
113class connection_pipeline;
115struct connection_stream_from;
116class connection_stream_to;
119} // namespace pqxx::internal::gate
120
121
122namespace pqxx
123{
125
137{
139
144 connection &conn;
145
147
151
153
156
158
169};
170
171
173
180enum skip_init : int
181{
184
187
190};
191
192
194
217template<skip_init... SKIP> inline void skip_init_ssl() noexcept
218{
219 // (Normalise skip flags to one per.)
220 pqxx::internal::skip_init_ssl(((1 << SKIP) | ...));
221}
222
223
225
232using table_path = std::initializer_list<std::string_view>;
233
234
236enum class error_verbosity : int
237{
238 // These values must match those in libpq's PGVerbosity enum.
239 terse = 0,
242};
243
244
246
279class PQXX_LIBEXPORT connection
280{
281public:
282 connection() : connection{""} {}
283
285 explicit connection(char const options[])
286 {
288 init(options);
289 }
290
292 explicit connection(zview options) : connection{options.c_str()}
293 {
294 // (Delegates to other constructor which calls check_version for us.)
295 }
296
298
303 connection(connection &&rhs);
304
305#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_RANGES)
307
322 template<internal::ZKey_ZValues MAPPING>
323 inline connection(MAPPING const &params);
324#endif // PQXX_HAVE_CONCEPTS
325
326 ~connection()
327 {
328 try
329 {
330 close();
331 }
332 catch (std::exception const &)
333 {}
334 }
335
336 // TODO: Once we drop notification_receiver/errorhandler, move is easier.
338
341 connection &operator=(connection &&rhs);
342
343 connection(connection const &) = delete;
344 connection &operator=(connection const &) = delete;
345
347
352 [[nodiscard]] bool PQXX_PURE is_open() const noexcept;
353
355 void process_notice(char const[]) noexcept;
357
360 void process_notice(zview) noexcept;
361
363 void trace(std::FILE *) noexcept;
364
375
377 [[nodiscard]] char const *dbname() const;
378
380
381 [[nodiscard]] char const *username() const;
382
384
387 [[nodiscard]] char const *hostname() const;
388
390 [[nodiscard]] char const *port() const;
391
393 [[nodiscard]] int PQXX_PURE backendpid() const & noexcept;
394
396
406 [[nodiscard]] int PQXX_PURE sock() const & noexcept;
407
409
412 [[nodiscard]] int PQXX_PURE protocol_version() const noexcept;
413
415
427 [[nodiscard]] int PQXX_PURE server_version() const noexcept;
429
431
451
452 [[nodiscard]] std::string get_client_encoding() const;
453
455
458 void set_client_encoding(zview encoding) &
459 {
460 set_client_encoding(encoding.c_str());
461 }
462
464
467 void set_client_encoding(char const encoding[]) &;
468
470 [[nodiscard]] int encoding_id() const;
471
473
475
496 template<typename TYPE>
497 void set_session_var(std::string_view var, TYPE const &value) &
498 {
499 if constexpr (nullness<TYPE>::has_null)
500 {
501 if (nullness<TYPE>::is_null(value))
502 throw variable_set_to_null{
503 internal::concat("Attempted to set variable ", var, " to null.")};
504 }
505 exec(internal::concat("SET ", quote_name(var), "=", quote(value)));
506 }
507
509
515 std::string get_var(std::string_view var);
516
518
524 template<typename TYPE> TYPE get_var_as(std::string_view var)
525 {
526 return from_string<TYPE>(get_var(var));
527 }
528
619
637 int get_notifs();
638
640
654 int await_notification();
655
657
673 int await_notification(std::time_t seconds, long microseconds = 0);
674
676
683 using notification_handler = std::function<void(notification)>;
684
686
713 void listen(std::string_view channel, notification_handler handler = {});
714
716
747
748 [[nodiscard]] std::string
749 encrypt_password(zview user, zview password, zview algorithm)
750 {
751 return encrypt_password(user.c_str(), password.c_str(), algorithm.c_str());
752 }
754 [[nodiscard]] std::string encrypt_password(
755 char const user[], char const password[], char const *algorithm = nullptr);
757
800
802
806 void prepare(zview name, zview definition) &
807 {
808 prepare(name.c_str(), definition.c_str());
809 }
810
815 void prepare(char const name[], char const definition[]) &;
816
818 [[deprecated("Either name your statement, or just parameterise it.")]] void
819 prepare(char const definition[]) &;
820 [[deprecated("Either name your statement, or just parameterise it.")]] void
821 prepare(zview definition) &
822 {
824 prepare(definition.c_str());
826 }
827
829 void unprepare(std::string_view name);
830
832
833 // C++20: constexpr. Breaks ABI.
835
838 [[nodiscard]] std::string adorn_name(std::string_view);
839
844
846 [[nodiscard]] std::string esc(char const text[]) const
847 {
848 return esc(std::string_view{text});
849 }
850
851#if defined(PQXX_HAVE_SPAN)
853
864 [[nodiscard]] std::string_view
865 esc(std::string_view text, std::span<char> buffer)
866 {
867 auto const size{std::size(text)}, space{std::size(buffer)};
868 auto const needed{2 * size + 1};
869 if (space < needed)
870 throw range_error{internal::concat(
871 "Not enough room to escape string of ", size, " byte(s): need ",
872 needed, " bytes of buffer space, but buffer size is ", space, ".")};
873 auto const data{buffer.data()};
874 return {data, esc_to_buf(text, data)};
875 }
876#endif
877
879
882 [[nodiscard]] std::string esc(std::string_view text) const;
883
884#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_RANGES)
886
887 template<binary DATA> [[nodiscard]] std::string esc(DATA const &data) const
888 {
889 return esc_raw(data);
890 }
891#endif
892
893#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN)
895
906 template<binary DATA>
907 [[nodiscard]] zview esc(DATA const &data, std::span<char> buffer) const
908 {
909 auto const size{std::size(data)}, space{std::size(buffer)};
910 auto const needed{internal::size_esc_bin(std::size(data))};
911 if (space < needed)
912 throw range_error{internal::concat(
913 "Not enough room to escape binary string of ", size, " byte(s): need ",
914 needed, " bytes of buffer space, but buffer size is ", space, ".")};
915
916 bytes_view view{std::data(data), std::size(data)};
917 auto const out{std::data(buffer)};
918 // Actually, in the modern format, we know beforehand exactly how many
919 // bytes we're going to fill. Just leave out the trailing zero.
920 internal::esc_bin(view, out);
921 return zview{out, needed - 1};
922 }
923#endif
924
926 [[deprecated("Use std::byte for binary data.")]] std::string
927 esc_raw(unsigned char const bin[], std::size_t len) const;
928
930
931 [[nodiscard]] std::string esc_raw(bytes_view) const;
932
933#if defined(PQXX_HAVE_SPAN)
935
936 [[nodiscard]] std::string esc_raw(bytes_view, std::span<char> buffer) const;
937#endif
938
939#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_RANGES)
941
942 template<binary DATA>
943 [[nodiscard]] std::string esc_raw(DATA const &data) const
944 {
945 return esc_raw(bytes_view{std::data(data), std::size(data)});
946 }
947#endif
948
949#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN)
951 template<binary DATA>
952 [[nodiscard]] zview esc_raw(DATA const &data, std::span<char> buffer) const
953 {
954 return this->esc(binary_cast(data), buffer);
955 }
956#endif
957
958 // TODO: Make "into buffer" variant to eliminate a string allocation.
960
967 [[nodiscard]] bytes unesc_bin(std::string_view text) const
968 {
969 bytes buf;
970 buf.resize(pqxx::internal::size_unesc_bin(std::size(text)));
971 pqxx::internal::unesc_bin(text, buf.data());
972 return buf;
973 }
974
976 std::string quote_raw(bytes_view) const;
977
978#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_RANGES)
980
981 template<binary DATA>
982 [[nodiscard]] std::string quote_raw(DATA const &data) const
983 {
984 return quote_raw(bytes_view{std::data(data), std::size(data)});
985 }
986#endif
987
988 // TODO: Make "into buffer" variant to eliminate a string allocation.
990 [[nodiscard]] std::string quote_name(std::string_view identifier) const;
991
992 // TODO: Make "into buffer" variant to eliminate a string allocation.
994
997 [[nodiscard]] std::string quote_table(std::string_view name) const;
998
999 // TODO: Make "into buffer" variant to eliminate a string allocation.
1001
1009 [[nodiscard]] std::string quote_table(table_path) const;
1010
1011 // TODO: Make "into buffer" variant to eliminate a string allocation.
1013
1020 template<PQXX_CHAR_STRINGS_ARG STRINGS>
1021 inline std::string quote_columns(STRINGS const &columns) const;
1022
1023 // TODO: Make "into buffer" variant to eliminate a string allocation.
1025
1028 template<typename T>
1029 [[nodiscard]] inline std::string quote(T const &t) const;
1030
1031 [[deprecated("Use std::byte for binary data.")]] std::string
1032 quote(binarystring const &) const;
1033
1034 // TODO: Make "into buffer" variant to eliminate a string allocation.
1036 [[nodiscard]] std::string quote(bytes_view bytes) const;
1037
1038 // TODO: Make "into buffer" variant to eliminate a string allocation.
1040
1065 [[nodiscard]] std::string
1066 esc_like(std::string_view text, char escape_char = '\\') const;
1067
1069
1073 [[deprecated("Use std::string_view or pqxx:zview.")]] std::string
1074 esc(char const text[], std::size_t maxlen) const
1075 {
1076 return esc(std::string_view{text, maxlen});
1077 }
1078
1080
1083 [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string
1084 unesc_raw(zview text) const
1085 {
1087 return unesc_raw(text.c_str());
1089 }
1090
1092
1095 [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string
1096 unesc_raw(char const text[]) const;
1097
1099 [[deprecated("Use quote(bytes_view).")]] std::string
1100 quote_raw(unsigned char const bin[], std::size_t len) const;
1102
1104
1108 void cancel_query();
1109
1110#if defined(_WIN32) || __has_include(<fcntl.h>)
1112
1116 void set_blocking(bool block) &;
1117#endif // defined(_WIN32) || __has_include(<fcntl.h>)
1118
1120
1132 void set_verbosity(error_verbosity verbosity) & noexcept;
1133
1134 // C++20: Use std::callable.
1135
1137
1149 void set_notice_handler(std::function<void(zview)> handler)
1150 {
1151 m_notice_waiters->notice_handler = std::move(handler);
1152 }
1153
1155
1160 [[nodiscard, deprecated("Use a notice handler instead.")]]
1161 std::vector<errorhandler *> get_errorhandlers() const;
1162
1164
1170 [[nodiscard]] std::string connection_string() const;
1171
1173
1181 void close();
1182
1184
1190 static connection seize_raw_connection(internal::pq::PGconn *raw_conn)
1191 {
1192 return connection{raw_conn};
1193 }
1194
1196
1201 internal::pq::PGconn *release_raw_connection() &&
1202 {
1203 return std::exchange(m_conn, nullptr);
1204 }
1205
1207
1220 [[deprecated("To set session variables, use set_session_var.")]] void
1221 set_variable(std::string_view var, std::string_view value) &;
1222
1224
1227 [[deprecated("Use get_var instead.")]] std::string
1228 get_variable(std::string_view);
1229
1230private:
1231 friend class connecting;
1232 enum connect_mode
1233 {
1234 connect_nonblocking
1235 };
1236 connection(connect_mode, zview connection_string);
1237
1239 explicit connection(internal::pq::PGconn *raw_conn);
1240
1242
1247 std::pair<bool, bool> poll_connect();
1248
1249 // Initialise based on connection string.
1250 void init(char const options[]);
1251 // Initialise based on parameter names and values.
1252 void init(char const *params[], char const *values[]);
1253 void set_up_notice_handlers();
1254 void complete_init();
1255
1256 result make_result(
1257 internal::pq::PGresult *pgr, std::shared_ptr<std::string> const &query,
1258 std::string_view desc = ""sv);
1259
1260 void PQXX_PRIVATE set_up_state();
1261
1262 int PQXX_PRIVATE PQXX_PURE status() const noexcept;
1263
1265
1269 std::size_t esc_to_buf(std::string_view text, char *buf) const;
1270
1271 friend class internal::gate::const_connection_largeobject;
1272 char const *PQXX_PURE err_msg() const noexcept;
1273
1274 result exec_prepared(std::string_view statement, internal::c_params const &);
1275
1277 void check_movable() const;
1279 void check_overwritable() const;
1280
1281 friend class internal::gate::connection_errorhandler;
1282 void PQXX_PRIVATE register_errorhandler(errorhandler *);
1283 void PQXX_PRIVATE unregister_errorhandler(errorhandler *) noexcept;
1284
1285 friend class internal::gate::connection_transaction;
1286 result exec(std::string_view, std::string_view = ""sv);
1287 result PQXX_PRIVATE
1288 exec(std::shared_ptr<std::string> const &, std::string_view = ""sv);
1289 void PQXX_PRIVATE register_transaction(transaction_base *);
1290 void PQXX_PRIVATE unregister_transaction(transaction_base *) noexcept;
1291
1292 friend struct internal::gate::connection_stream_from;
1294
1299 std::pair<std::unique_ptr<char, void (*)(void const *)>, std::size_t>
1300 read_copy_line();
1301
1302 friend class internal::gate::connection_stream_to;
1303 void PQXX_PRIVATE write_copy_line(std::string_view);
1304 void PQXX_PRIVATE end_copy_write();
1305
1306 friend class internal::gate::connection_largeobject;
1307 internal::pq::PGconn *raw_connection() const { return m_conn; }
1308
1309 friend class internal::gate::connection_notification_receiver;
1310 void add_receiver(notification_receiver *);
1311 void remove_receiver(notification_receiver *) noexcept;
1312
1313 friend class internal::gate::connection_pipeline;
1314 void PQXX_PRIVATE start_exec(char const query[]);
1315 bool PQXX_PRIVATE consume_input() noexcept;
1316 bool PQXX_PRIVATE is_busy() const noexcept;
1317 internal::pq::PGresult *get_result();
1318
1319 friend class internal::gate::connection_dbtransaction;
1320 friend class internal::gate::connection_sql_cursor;
1321
1322 result exec_params(std::string_view query, internal::c_params const &args);
1323
1325 internal::pq::PGconn *m_conn = nullptr;
1326
1328
1335 transaction_base const *m_trans = nullptr;
1336
1338 std::shared_ptr<pqxx::internal::notice_waiters> m_notice_waiters;
1339
1340 // TODO: Remove these when we retire notification_receiver.
1341 // TODO: Can we make these movable?
1342 using receiver_list =
1343 std::multimap<std::string, pqxx::notification_receiver *>;
1345 receiver_list m_receivers;
1346
1348
1355 std::map<std::string, notification_handler> m_notification_handlers;
1356
1358 int m_unique_id = 0;
1359};
1360
1361
1363using connection_base = connection;
1364
1365
1367
1410class PQXX_LIBEXPORT connecting
1411{
1412public:
1414 connecting(zview connection_string = ""_zv);
1415
1416 connecting(connecting const &) = delete;
1417 connecting(connecting &&) = default;
1418 connecting &operator=(connecting const &) = delete;
1419 connecting &operator=(connecting &&) = default;
1420
1422 [[nodiscard]] int sock() const & noexcept { return m_conn.sock(); }
1423
1425 [[nodiscard]] constexpr bool wait_to_read() const & noexcept
1426 {
1427 return m_reading;
1428 }
1429
1431 [[nodiscard]] constexpr bool wait_to_write() const & noexcept
1432 {
1433 return m_writing;
1434 }
1435
1437 void process() &;
1438
1440 [[nodiscard]] constexpr bool done() const & noexcept
1441 {
1442 return not m_reading and not m_writing;
1443 }
1444
1446
1454 [[nodiscard]] connection produce() &&;
1455
1456private:
1457 connection m_conn;
1458 bool m_reading{false};
1459 bool m_writing{true};
1460};
1461
1462
1463template<typename T> inline std::string connection::quote(T const &t) const
1464{
1465 if constexpr (nullness<T>::always_null)
1466 {
1467 return "NULL";
1468 }
1469 else
1470 {
1471 if (is_null(t))
1472 return "NULL";
1473 auto const text{to_string(t)};
1474
1475 // Okay, there's an easy way to do this and there's a hard way. The easy
1476 // way was "quote, esc(to_string(t)), quote". I'm going with the hard way
1477 // because it's going to save some string manipulation that will probably
1478 // incur some unnecessary memory allocations and deallocations.
1479 std::string buf{'\''};
1480 buf.resize(2 + 2 * std::size(text) + 1);
1481 auto const content_bytes{esc_to_buf(text, buf.data() + 1)};
1482 auto const closing_quote{1 + content_bytes};
1483 buf[closing_quote] = '\'';
1484 auto const end{closing_quote + 1};
1485 buf.resize(end);
1486 return buf;
1487 }
1488}
1489
1490
1491template<PQXX_CHAR_STRINGS_ARG STRINGS>
1492inline std::string connection::quote_columns(STRINGS const &columns) const
1493{
1494 return separated_list(
1495 ","sv, std::cbegin(columns), std::cend(columns),
1496 [this](auto col) { return this->quote_name(*col); });
1497}
1498
1499
1500#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_RANGES)
1501template<internal::ZKey_ZValues MAPPING>
1502inline connection::connection(MAPPING const &params)
1503{
1504 check_version();
1505
1506 std::vector<char const *> keys, values;
1507 if constexpr (std::ranges::sized_range<MAPPING>)
1508 {
1509 auto const size{std::ranges::size(params) + 1};
1510 keys.reserve(size);
1511 values.reserve(size);
1512 }
1513 for (auto const &[key, value] : params)
1514 {
1515 keys.push_back(internal::as_c_string(key));
1516 values.push_back(internal::as_c_string(value));
1517 }
1518 keys.push_back(nullptr);
1519 values.push_back(nullptr);
1520 init(std::data(keys), std::data(values));
1521}
1522#endif // PQXX_HAVE_CONCEPTS
1523
1524
1526[[nodiscard,
1527 deprecated("Use connection::encrypt_password instead.")]] std::string
1529 encrypt_password(char const user[], char const password[]);
1530
1532[[nodiscard,
1533 deprecated("Use connection::encrypt_password instead.")]] inline std::string
1540} // namespace pqxx
1541#endif
Definition connection-errorhandler.hxx:12
Definition connection-largeobject.hxx:16
Definition connection-sql_cursor.hxx:12
Definition connection-transaction.hxx:11
Definition connection-largeobject.hxx:27
Cursor with SQL positioning semantics.
Definition sql_cursor.hxx:32
Marker-type wrapper: zero-terminated std::string_view.
Definition zview.hxx:38
constexpr char const * c_str() const &noexcept
Either a null pointer, or a zero-terminated text buffer.
Definition zview.hxx:96
#define PQXX_COLD
Definition header-pre.hxx:87
#define PQXX_LIBEXPORT
Definition header-pre.hxx:157
#define PQXX_PURE
Declare function "pure": no side effects, only reads globals and its args.
Definition header-pre.hxx:77
#define PQXX_PRIVATE
Definition header-pre.hxx:158
Definition connection.hxx:108
Internal items for libpqxx' own use. Do not use these yourself.
Definition encodings.cxx:33
void PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data, std::byte buffer[])
Reconstitute binary data from its escaped version.
Definition util.cxx:165
constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
Compute binary size from the size of its escaped version.
Definition util.hxx:525
The home of all libpqxx classes, functions, templates, etc.
Definition array.cxx:27
std::string encrypt_password(zview user, zview password)
Encrypt password.
Definition connection.hxx:1534
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:46
PQXX_PRIVATE void check_version() noexcept
Definition util.hxx:236
std::string char const password[]
Definition connection.hxx:1529
std::conditional< has_generic_bytes_char_traits, std::basic_string_view< std::byte >, std::basic_string_view< std::byte, byte_char_traits > >::type bytes_view
Type alias for a view of bytes.
Definition util.hxx:383
std::string to_string(T const &value)
Definition conversions.hxx:1249
std::conditional< has_generic_bytes_char_traits, std::basic_string< std::byte >, std::basic_string< std::byte, byte_char_traits > >::type bytes
Type alias for a container containing bytes.
Definition util.hxx:373
void skip_init_ssl() noexcept
Control initialisation of OpenSSL and libcrypto libraries.
Definition connection.hxx:217
bytes_view binary_cast(TYPE const &data)
End a code block started by "ignore-deprecated-pre.hxx".
Definition util.hxx:409
std::initializer_list< std::string_view > table_path
Representation of a PostgreSQL table path.
Definition connection.hxx:232
connection & conn
The connection which received the notification.
Definition connection.hxx:144
constexpr bool is_null(TYPE const &value) noexcept
Is value null?
Definition strconv.hxx:516
int backend_pid
Process ID of the backend that sent the notification.
Definition connection.hxx:168
skip_init
Flags for skipping initialisation of SSL-related libraries.
Definition connection.hxx:181
@ crypto
Skip initialisation of libcrypto.
Definition connection.hxx:189
@ openssl
Skip initialisation of OpenSSL library.
Definition connection.hxx:186
@ nothing
A do-nothing flag that does not affect anything.
Definition connection.hxx:183
zview channel
Channel name.
Definition connection.hxx:150
connection connection_base
Definition connection.hxx:1363
zview payload
Optional payload text.
Definition connection.hxx:155
error_verbosity
Error verbosity levels.
Definition connection.hxx:237
@ terse
Definition connection.hxx:239
@ verbose
Definition connection.hxx:241
@ normal
Definition connection.hxx:240
@ text
Definition types.hxx:71
An incoming notification.
Definition connection.hxx:137