libpqxx 7.7.5
connection.hxx
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-2023, 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 <string_view>
28#include <tuple>
29
30// Double-check in order to suppress an overzealous Visual C++ warning (#418).
31#if defined(PQXX_HAVE_CONCEPTS) && __has_include(<ranges>)
32# include <ranges>
33#endif
34
35#include "pqxx/errorhandler.hxx"
36#include "pqxx/except.hxx"
37#include "pqxx/internal/concat.hxx"
38#include "pqxx/params.hxx"
39#include "pqxx/separated_list.hxx"
40#include "pqxx/strconv.hxx"
41#include "pqxx/types.hxx"
42#include "pqxx/util.hxx"
43#include "pqxx/zview.hxx"
44
45
76namespace pqxx::internal
77{
78class sql_cursor;
79
80#if defined(PQXX_HAVE_CONCEPTS)
82template<typename T>
83concept ZKey_ZValues = std::ranges::input_range<T> and requires(T t)
84{
85 {std::cbegin(t)};
86 {
87 std::get<0>(*std::cbegin(t))
88 } -> ZString;
89 {
90 std::get<1>(*std::cbegin(t))
91 } -> ZString;
92} and std::tuple_size_v<typename std::ranges::iterator_t<T>::value_type>
93== 2;
94#endif // PQXX_HAVE_CONCEPTS
95} // namespace pqxx::internal
96
97
99{
100class connection_dbtransaction;
101class connection_errorhandler;
102class connection_largeobject;
103class connection_notification_receiver;
104class connection_pipeline;
105class connection_sql_cursor;
106class connection_stream_from;
107class connection_stream_to;
108class connection_transaction;
109class const_connection_largeobject;
110} // namespace pqxx::internal::gate
111
112
113namespace pqxx
114{
116
123using table_path = std::initializer_list<std::string_view>;
124
125
127[[nodiscard,
128 deprecated("Use connection::encrypt_password instead.")]] std::string
129 PQXX_LIBEXPORT
130 encrypt_password(char const user[], char const password[]);
131
133[[nodiscard,
134 deprecated("Use connection::encrypt_password instead.")]] inline std::string
136{
137#include "pqxx/internal/ignore-deprecated-pre.hxx"
138 return encrypt_password(user.c_str(), password.c_str());
139#include "pqxx/internal/ignore-deprecated-post.hxx"
140}
141
142
144enum class error_verbosity : int
145{
146 // These values must match those in libpq's PGVerbosity enum.
147 terse = 0,
148 normal = 1,
149 verbose = 2
150};
151
152
154
184class PQXX_LIBEXPORT connection
185{
186public:
188
190 explicit connection(char const options[])
191 {
193 init(options);
194 }
195
197 explicit connection(zview options) : connection{options.c_str()}
198 {
199 // (Delegates to other constructor which calls check_version for us.)
200 }
201
203
208 connection(connection &&rhs);
209
210#if defined(PQXX_HAVE_CONCEPTS)
212
227 template<internal::ZKey_ZValues MAPPING>
228 inline connection(MAPPING const &params);
229#endif // PQXX_HAVE_CONCEPTS
230
232 {
233 try
234 {
235 close();
236 }
237 catch (std::exception const &)
238 {}
239 }
240
242
245 connection &operator=(connection &&rhs);
246
247 connection(connection const &) = delete;
248 connection &operator=(connection const &) = delete;
249
251
256 [[nodiscard]] bool PQXX_PURE is_open() const noexcept;
257
259 void process_notice(char const[]) noexcept;
261
264 void process_notice(zview) noexcept;
265
267 void trace(std::FILE *) noexcept;
268
280 [[nodiscard]] char const *dbname() const;
281
283 [[nodiscard]] char const *username() const;
284
286 [[nodiscard]] char const *hostname() const;
287
289 [[nodiscard]] char const *port() const;
290
292 [[nodiscard]] int PQXX_PURE backendpid() const &noexcept;
293
295
305 [[nodiscard]] int PQXX_PURE sock() const &noexcept;
306
308
311 [[nodiscard]] int PQXX_PURE protocol_version() const noexcept;
312
314
326 [[nodiscard]] int PQXX_PURE server_version() const noexcept;
328
330
351 [[nodiscard]] std::string get_client_encoding() const;
352
354
357 void set_client_encoding(zview encoding) &
358 {
359 set_client_encoding(encoding.c_str());
360 }
361
363
366 void set_client_encoding(char const encoding[]) &;
367
369 [[nodiscard]] int PQXX_PRIVATE encoding_id() const;
370
372
374
387 [[deprecated("To set session variables, use set_session_var.")]] void
388 set_variable(std::string_view var, std::string_view value) &;
389
391
412 template<typename TYPE>
413 void set_session_var(std::string_view var, TYPE const &value) &
414 {
415 if constexpr (nullness<TYPE>::has_null)
416 {
417 if (nullness<TYPE>::is_null(value))
419 internal::concat("Attempted to set variable ", var, " to null.")};
420 }
421 exec(internal::concat("SET ", quote_name(var), "=", quote(value)));
422 }
423
425
428 [[deprecated("Use get_var instead.")]] std::string
429 get_variable(std::string_view);
430
432
438 std::string get_var(std::string_view var);
439
441
447 template<typename TYPE> TYPE get_var_as(std::string_view var)
448 {
449 return from_string<TYPE>(get_var(var));
450 }
451
457
474 int get_notifs();
475
477
489 int await_notification();
490
492
504 int await_notification(std::time_t seconds, long microseconds);
506
538 [[nodiscard]] std::string
539 encrypt_password(zview user, zview password, zview algorithm)
540 {
541 return encrypt_password(user.c_str(), password.c_str(), algorithm.c_str());
542 }
544 [[nodiscard]] std::string encrypt_password(
545 char const user[], char const password[], char const *algorithm = nullptr);
547
590
592
596 void prepare(zview name, zview definition) &
597 {
598 prepare(name.c_str(), definition.c_str());
599 }
600
605 void prepare(char const name[], char const definition[]) &;
606
608
615 void prepare(char const definition[]) &;
616 void prepare(zview definition) &
617 {
618 return prepare(definition.c_str());
619 }
620
622 void unprepare(std::string_view name);
623
625
626 // C++20: constexpr. Breaks ABI.
628
631 [[nodiscard]] std::string adorn_name(std::string_view);
632
637
639
643 [[deprecated("Use std::string_view or pqxx:zview.")]] std::string
644 esc(char const text[], std::size_t maxlen) const
645 {
646 return esc(std::string_view{text, maxlen});
647 }
648
650 [[nodiscard]] std::string esc(char const text[]) const
651 {
652 return esc(std::string_view{text});
653 }
654
655#if defined(PQXX_HAVE_SPAN)
657
668 [[nodiscard]] std::string_view
669 esc(std::string_view text, std::span<char> buffer)
670 {
671 auto const size{std::size(text)}, space{std::size(buffer)};
672 auto const needed{2 * size + 1};
673 if (space < needed)
674 throw range_error{internal::concat(
675 "Not enough room to escape string of ", size, " byte(s): need ",
676 needed, " bytes of buffer space, but buffer size is ", space, ".")};
677 auto const data{buffer.data()};
678 return {data, esc_to_buf(text, data)};
679 }
680#endif
681
683
686 [[nodiscard]] std::string esc(std::string_view text) const;
687
688#if defined(PQXX_HAVE_CONCEPTS)
690
691 template<binary DATA> [[nodiscard]] std::string esc(DATA const &data) const
692 {
693 return esc_raw(data);
694 }
695#endif
696
697#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN)
699
710 template<binary DATA>
711 [[nodiscard]] zview esc(DATA const &data, std::span<char> buffer) const
712 {
713 auto const size{std::size(data)}, space{std::size(buffer)};
714 auto const needed{internal::size_esc_bin(std::size(data))};
715 if (space < needed)
716 throw range_error{internal::concat(
717 "Not enough room to escape binary string of ", size, " byte(s): need ",
718 needed, " bytes of buffer space, but buffer size is ", space, ".")};
719
720 std::basic_string_view<std::byte> view{std::data(data), std::size(data)};
721 auto const out{std::data(buffer)};
722 // Actually, in the modern format, we know beforehand exactly how many
723 // bytes we're going to fill. Just leave out the trailing zero.
724 internal::esc_bin(view, out);
725 return zview{out, needed - 1};
726 }
727#endif
728
730 [[deprecated("Use std::byte for binary data.")]] std::string
731 esc_raw(unsigned char const bin[], std::size_t len) const;
732
734
735 [[nodiscard]] std::string esc_raw(std::basic_string_view<std::byte>) const;
736
737#if defined(PQXX_HAVE_SPAN)
739
740 [[nodiscard]] std::string
741 esc_raw(std::basic_string_view<std::byte>, std::span<char> buffer) const;
742#endif
743
744#if defined(PQXX_HAVE_CONCEPTS)
746
747 template<binary DATA>
748 [[nodiscard]] std::string esc_raw(DATA const &data) const
749 {
750 return esc_raw(
751 std::basic_string_view<std::byte>{std::data(data), std::size(data)});
752 }
753#endif
754
755#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN)
757 template<binary DATA>
758 [[nodiscard]] zview esc_raw(DATA const &data, std::span<char> buffer) const
759 {
760 return this->esc(binary_cast(data), buffer);
761 }
762#endif
763
765
768 [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string
769 unesc_raw(zview text) const
770 {
771#include "pqxx/internal/ignore-deprecated-pre.hxx"
772 return unesc_raw(text.c_str());
773#include "pqxx/internal/ignore-deprecated-post.hxx"
774 }
775
777
780 [[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string
781 unesc_raw(char const text[]) const;
782
783 // TODO: Make "into buffer" variant to eliminate a string allocation.
785
792 [[nodiscard]] std::basic_string<std::byte>
793 unesc_bin(std::string_view text) const
794 {
795 std::basic_string<std::byte> buf;
796 buf.resize(pqxx::internal::size_unesc_bin(std::size(text)));
797 pqxx::internal::unesc_bin(text, buf.data());
798 return buf;
799 }
800
802 [[deprecated("Use quote(std::basic_string_view<std::byte>).")]] std::string
803 quote_raw(unsigned char const bin[], std::size_t len) const;
804
806 std::string quote_raw(std::basic_string_view<std::byte>) const;
807
808#if defined(PQXX_HAVE_CONCEPTS)
810
811 template<binary DATA>
812 [[nodiscard]] std::string quote_raw(DATA const &data) const
813 {
814 return quote_raw(
815 std::basic_string_view<std::byte>{std::data(data), std::size(data)});
816 }
817#endif
818
819 // TODO: Make "into buffer" variant to eliminate a string allocation.
821 [[nodiscard]] std::string quote_name(std::string_view identifier) const;
822
823 // TODO: Make "into buffer" variant to eliminate a string allocation.
825
828 [[nodiscard]] std::string quote_table(std::string_view name) const;
829
830 // TODO: Make "into buffer" variant to eliminate a string allocation.
832
840 [[nodiscard]] std::string quote_table(table_path) const;
841
842 // TODO: Make "into buffer" variant to eliminate a string allocation.
844
851 template<PQXX_CHAR_STRINGS_ARG STRINGS>
852 inline std::string quote_columns(STRINGS const &columns) const;
853
854 // TODO: Make "into buffer" variant to eliminate a string allocation.
856
859 template<typename T>
860 [[nodiscard]] inline std::string quote(T const &t) const;
861
862 [[deprecated("Use std::byte for binary data.")]] std::string
863 quote(binarystring const &) const;
864
865 // TODO: Make "into buffer" variant to eliminate a string allocation.
867 [[nodiscard]] std::string
868 quote(std::basic_string_view<std::byte> bytes) const;
869
870 // TODO: Make "into buffer" variant to eliminate a string allocation.
872
897 [[nodiscard]] std::string
898 esc_like(std::string_view text, char escape_char = '\\') const;
900
902
906 void cancel_query();
907
908#if defined(_WIN32) || __has_include(<fcntl.h>)
910
914 void set_blocking(bool block) &;
915#endif // defined(_WIN32) || __has_include(<fcntl.h>)
916
918
927 void set_verbosity(error_verbosity verbosity) &noexcept;
928
930
942 [[nodiscard]] std::vector<errorhandler *> get_errorhandlers() const;
943
945
951 [[nodiscard]] std::string connection_string() const;
952
954
962 void close();
963
965
971 static connection seize_raw_connection(internal::pq::PGconn *raw_conn)
972 {
973 return connection{raw_conn};
974 }
975
977
982 internal::pq::PGconn *release_raw_connection() &&
983 {
984 return std::exchange(m_conn, nullptr);
985 }
986
987private:
988 friend class connecting;
989 enum connect_mode
990 {
991 connect_nonblocking
992 };
993 connection(connect_mode, zview connection_string);
994
996 explicit connection(internal::pq::PGconn *raw_conn) : m_conn{raw_conn} {}
997
999
1004 std::pair<bool, bool> poll_connect();
1005
1006 // Initialise based on connection string.
1007 void init(char const options[]);
1008 // Initialise based on parameter names and values.
1009 void init(char const *params[], char const *values[]);
1010 void complete_init();
1011
1012 result make_result(
1013 internal::pq::PGresult *pgr, std::shared_ptr<std::string> const &query,
1014 std::string_view desc = ""sv);
1015
1016 void PQXX_PRIVATE set_up_state();
1017
1018 int PQXX_PRIVATE PQXX_PURE status() const noexcept;
1019
1021
1025 std::size_t esc_to_buf(std::string_view text, char *buf) const;
1026
1027 friend class internal::gate::const_connection_largeobject;
1028 char const *PQXX_PURE err_msg() const noexcept;
1029
1030 void PQXX_PRIVATE process_notice_raw(char const msg[]) noexcept;
1031
1032 result exec_prepared(std::string_view statement, internal::c_params const &);
1033
1035 void check_movable() const;
1037 void check_overwritable() const;
1038
1039 friend class internal::gate::connection_errorhandler;
1040 void PQXX_PRIVATE register_errorhandler(errorhandler *);
1041 void PQXX_PRIVATE unregister_errorhandler(errorhandler *) noexcept;
1042
1043 friend class internal::gate::connection_transaction;
1044 result exec(std::string_view, std::string_view = ""sv);
1045 result
1046 PQXX_PRIVATE exec(std::shared_ptr<std::string>, std::string_view = ""sv);
1047 void PQXX_PRIVATE register_transaction(transaction_base *);
1048 void PQXX_PRIVATE unregister_transaction(transaction_base *) noexcept;
1049
1050 friend class internal::gate::connection_stream_from;
1051 std::pair<std::unique_ptr<char, std::function<void(char *)>>, std::size_t>
1052 PQXX_PRIVATE read_copy_line();
1053
1054 friend class internal::gate::connection_stream_to;
1055 void PQXX_PRIVATE write_copy_line(std::string_view);
1056 void PQXX_PRIVATE end_copy_write();
1057
1058 friend class internal::gate::connection_largeobject;
1059 internal::pq::PGconn *raw_connection() const
1060 {
1061 return m_conn;
1062 }
1063
1064 friend class internal::gate::connection_notification_receiver;
1065 void add_receiver(notification_receiver *);
1066 void remove_receiver(notification_receiver *) noexcept;
1067
1068 friend class internal::gate::connection_pipeline;
1069 void PQXX_PRIVATE start_exec(char const query[]);
1070 bool PQXX_PRIVATE consume_input() noexcept;
1071 bool PQXX_PRIVATE is_busy() const noexcept;
1072 internal::pq::PGresult *get_result();
1073
1074 friend class internal::gate::connection_dbtransaction;
1075 friend class internal::gate::connection_sql_cursor;
1076
1077 result exec_params(std::string_view query, internal::c_params const &args);
1078
1080 internal::pq::PGconn *m_conn = nullptr;
1081
1083
1090 transaction_base const *m_trans = nullptr;
1091
1092 std::list<errorhandler *> m_errorhandlers;
1093
1094 using receiver_list =
1095 std::multimap<std::string, pqxx::notification_receiver *>;
1097 receiver_list m_receivers;
1098
1100 int m_unique_id = 0;
1101};
1102
1103
1106
1107
1109
1152class PQXX_LIBEXPORT connecting
1153{
1154public:
1156 connecting(zview connection_string = ""_zv);
1157
1158 connecting(connecting const &) = delete;
1159 connecting(connecting &&) = default;
1160 connecting &operator=(connecting const &) = delete;
1162
1164 [[nodiscard]] int sock() const &noexcept { return m_conn.sock(); }
1165
1167 [[nodiscard]] constexpr bool wait_to_read() const &noexcept
1168 {
1169 return m_reading;
1170 }
1171
1173 [[nodiscard]] constexpr bool wait_to_write() const &noexcept
1174 {
1175 return m_writing;
1176 }
1177
1179 void process() &;
1180
1182 [[nodiscard]] constexpr bool done() const &noexcept
1183 {
1184 return not m_reading and not m_writing;
1185 }
1186
1188
1196 [[nodiscard]] connection produce() &&;
1197
1198private:
1199 connection m_conn;
1200 bool m_reading{false};
1201 bool m_writing{true};
1202};
1203
1204
1205template<typename T> inline std::string connection::quote(T const &t) const
1206{
1207 if constexpr (nullness<T>::always_null)
1208 {
1209 return "NULL";
1210 }
1211 else
1212 {
1213 if (is_null(t))
1214 return "NULL";
1215 auto const text{to_string(t)};
1216
1217 // Okay, there's an easy way to do this and there's a hard way. The easy
1218 // way was "quote, esc(to_string(t)), quote". I'm going with the hard way
1219 // because it's going to save some string manipulation that will probably
1220 // incur some unnecessary memory allocations and deallocations.
1221 std::string buf{'\''};
1222 buf.resize(2 + 2 * std::size(text) + 1);
1223 auto const content_bytes{esc_to_buf(text, buf.data() + 1)};
1224 auto const closing_quote{1 + content_bytes};
1225 buf[closing_quote] = '\'';
1226 auto const end{closing_quote + 1};
1227 buf.resize(end);
1228 return buf;
1229 }
1230}
1231
1232
1233template<PQXX_CHAR_STRINGS_ARG STRINGS>
1234inline std::string connection::quote_columns(STRINGS const &columns) const
1235{
1236 return separated_list(
1237 ","sv, std::cbegin(columns), std::cend(columns),
1238 [this](auto col) { return this->quote_name(*col); });
1239}
1240
1241
1242#if defined(PQXX_HAVE_CONCEPTS)
1243template<internal::ZKey_ZValues MAPPING>
1244inline connection::connection(MAPPING const &params)
1245{
1246 check_version();
1247
1248 std::vector<char const *> keys, values;
1249 if constexpr (std::ranges::sized_range<MAPPING>)
1250 {
1251 auto const size{std::ranges::size(params) + 1};
1252 keys.reserve(size);
1253 values.reserve(size);
1254 }
1255 for (auto const &[key, value] : params)
1256 {
1257 keys.push_back(internal::as_c_string(key));
1258 values.push_back(internal::as_c_string(value));
1259 }
1260 keys.push_back(nullptr);
1261 values.push_back(nullptr);
1262 init(std::data(keys), std::data(values));
1263}
1264#endif // PQXX_HAVE_CONCEPTS
1265} // namespace pqxx
1266#endif
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:27
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:43
std::string encrypt_password(char const user[], char const password[])
Encrypt a password.
Definition: connection.cxx:80
std::basic_string_view< std::byte > binary_cast(TYPE const &data)
Cast binary data to a type that libpqxx will recognise as binary.
Definition: util.hxx:306
std::initializer_list< std::string_view > table_path
Representation of a PostgreSQL table path.
Definition: connection.hxx:123
void check_version() noexcept
Definition: util.hxx:237
constexpr bool is_null(TYPE const &value) noexcept
Is value null?
Definition: strconv.hxx:370
std::string to_string(field const &value)
Convert a field to a string.
Definition: result.cxx:533
error_verbosity
Error verbosity levels.
Definition: connection.hxx:145
Internal items for libpqxx' own use. Do not use these yourself.
Definition: composite.hxx:83
void unesc_bin(std::string_view escaped_data, std::byte buffer[])
Reconstitute binary data from its escaped version.
Definition: util.cxx:158
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:422
Definition: connection.hxx:99
Connection to a database.
Definition: connection.hxx:185
std::string encrypt_password(zview user, zview password, zview algorithm)
Encrypt a password for a given user.
Definition: connection.hxx:539
void prepare(zview definition) &
Definition: connection.hxx:616
connection(char const options[])
Connect to a database, using options string.
Definition: connection.hxx:190
connection & operator=(connection const &)=delete
TYPE get_var_as(std::string_view var)
Read currently applicable value of a variable.
Definition: connection.hxx:447
connection()
Definition: connection.hxx:187
~connection()
Definition: connection.hxx:231
connection(zview options)
Connect to a database, using options string.
Definition: connection.hxx:197
internal::pq::PGconn * release_raw_connection() &&
Release the raw connection without closing it.
Definition: connection.hxx:982
static connection seize_raw_connection(internal::pq::PGconn *raw_conn)
Seize control of a raw libpq connection.
Definition: connection.hxx:971
std::basic_string< std::byte > unesc_bin(std::string_view text) const
Unescape binary data, e.g. from a table field or notification payload.
Definition: connection.hxx:793
std::string esc(char const text[]) const
Escape string for use as SQL string literal on this connection.
Definition: connection.hxx:650
std::string unesc_raw(zview text) const
Unescape binary data, e.g. from a table field or notification payload.
Definition: connection.hxx:769
std::string esc(char const text[], std::size_t maxlen) const
Escape string for use as SQL string literal on this connection.
Definition: connection.hxx:644
void prepare(zview name, zview definition) &
Define a prepared statement.
Definition: connection.hxx:596
void set_session_var(std::string_view var, TYPE const &value) &
Set one of the session variables to a new value.
Definition: connection.hxx:413
connection(connection const &)=delete
An ongoing, non-blocking stepping stone to a connection.
Definition: connection.hxx:1153
connecting(connecting &&)=default
int sock() const &noexcept
Get the socket. The socket may change during the connection process.
Definition: connection.hxx:1164
constexpr bool done() const &noexcept
Is our connection finished?
Definition: connection.hxx:1182
connecting & operator=(connecting const &)=delete
connecting(connecting const &)=delete
constexpr bool wait_to_write() const &noexcept
Should we currently wait to be able to write to the socket?
Definition: connection.hxx:1173
void process() &
Progress towards completion (but don't block).
connecting & operator=(connecting &&)=default
constexpr bool wait_to_read() const &noexcept
Should we currently wait to be able to read from the socket?
Definition: connection.hxx:1167
connecting(zview connection_string=""_zv)
Start connecting.
connection produce() &&
Produce the completed connection object.
Base class for error-handler callbacks.
Definition: errorhandler.hxx:54
The caller attempted to set a variable to null, which is not allowed.
Definition: except.hxx:77
Definition: notification.hxx:57
Build a parameter list for a parameterised or prepared statement.
Definition: params.hxx:220
Result set containing data returned by a query or command.
Definition: result.hxx:74
Traits describing a type's "null value," if any.
Definition: strconv.hxx:93
Interface definition (and common code) for "transaction" classes.
Definition: transaction_base.hxx:77
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:92