userver: userver/storages/postgres/exceptions.hpp Source File
Loading...
Searching...
No Matches
exceptions.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/exceptions.hpp
4/// @brief Postgres errors
5
6#include <stdexcept>
7#include <string_view>
8
9#include <fmt/format.h>
10
11#include <userver/storages/postgres/dsn.hpp>
12#include <userver/storages/postgres/io/traits.hpp>
13#include <userver/storages/postgres/message.hpp>
14
15#include <userver/compiler/demangle.hpp>
16#include <userver/utils/underlying_value.hpp>
17
18USERVER_NAMESPACE_BEGIN
19
20namespace storages::postgres {
21
22/**
23 * @page pg_errors uPg: Postgres errors
24 *
25 * Base class for all PostgreSQL errors is Error which is derived from
26 * std::runtime_error. This is done to simplify exception handling.
27 *
28 * There are two base types of errors: runtime (RuntimeError) and logic
29 * (LogicError).
30 *
31 * **Logic errors** are a consequence of faulty logic within the program such as
32 * violating logical preconditions or invariants and may be preventable by
33 * correcting the program.
34 *
35 * **Runtime errors** are due to events beyond the scope of the program, such as
36 * network failure, faulty configuration file, unique index violation etc. A
37 * user can catch such an error and recover from it by reconnecting, providing
38 * a decent default for configuration or modifying the key value.
39 *
40 * Both logic and runtime errors can contain a postgres server message
41 * (Message). Those are ServerLogicError and ServerRuntimeError
42 * respectively. These errors occur on the server side and are translated into
43 * exceptions by the driver. Server errors are descendants of either
44 * ServerLogicError or ServerRuntimeError and their hierarchy corresponds to SQL
45 * error classes.
46 *
47 * Some server errors, such as IntegrityConstraintViolation, have a more
48 * detailed hierarchy to distinguish errors in catch clauses.
49 *
50 * Server errors have the following hierarchy:
51 * - ServerLogicError
52 * - SqlStatementNotYetComplete
53 * - FeatureNotSupported
54 * - InvalidRoleSpecification
55 * - CardinalityViolation
56 * - InvalidObjectName
57 * - InvalidAuthorizationSpecification
58 * - SyntaxError
59 * - AccessRuleViolation
60 * - ServerRuntimeError
61 * - TriggeredActionException
62 * - LocatorException
63 * - InvalidGrantor
64 * - DiagnosticsException
65 * - DataException
66 * - DuplicatePreparedStatement
67 * - IntegrityConstraintViolation
68 * - RestrictViolation
69 * - NotNullViolation (TODO Make it a logic error)
70 * - ForeignKeyViolation
71 * - UniqueViolation
72 * - CheckViolation
73 * - ExclusionViolation
74 * - TriggeredDataChangeViolation
75 * - WithCheckOptionViolation
76 * - InvalidCursorState
77 * - InvalidSqlStatementName
78 * - InvalidTransactionState
79 * - DependentPrivilegeDescriptorsStillExist
80 * - InvalidTransactionTermination
81 * - ExternalRoutineException
82 * - ExternalRoutineInvocationException
83 * - SavepointException
84 * - SqlRoutineException
85 * - TransactionRollback
86 * - InsufficientResources
87 * - ProgramLimitExceeded
88 * - ObjectNotInPrerequisiteState
89 * - OperatorIntervention
90 * - QueryCancelled
91 * - AdminShutdown
92 * - CrashShutdown
93 * - CannotConnectNow
94 * - DatabaseDropped
95 * - SystemError
96 * - SnapshotFailure
97 * - ConfigurationFileError
98 * - FdwError
99 * - PlPgSqlError
100 * - InternalServerError
101 *
102 * Besides server errors there are exceptions thrown by the driver itself,
103 * those are:
104 * - LogicError
105 * - ResultSetError
106 * - FieldIndexOutOfBounds
107 * - FieldNameDoesntExist
108 * - FieldTupleMismatch
109 * - FieldValueIsNull
110 * - InvalidBinaryBuffer
111 * - InvalidInputBufferSize
112 * - InvalidParserCategory
113 * - InvalidTupleSizeRequested
114 * - NonSingleColumnResultSet
115 * - NonSingleRowResultSet
116 * - NoBinaryParser
117 * - RowIndexOutOfBounds
118 * - TypeCannotBeNull
119 * - UnknownBufferCategory
120 * - UserTypeError
121 * - CompositeSizeMismatch
122 * - CompositeMemberTypeMismatch
123 * - ArrayError
124 * - DimensionMismatch
125 * - InvalidDimensions
126 * - NumericError
127 * - NumericOverflow
128 * - ValueIsNaN
129 * - InvalidRepresentation
130 * - InvalidInputFormat
131 * - EnumerationError
132 * - InvalidEnumerationLiteral
133 * - InvalidEnumerationValue
134 * - TransactionError
135 * - AlreadyInTransaction
136 * - NotInTransaction
137 * - UnsupportedInterval
138 * - BoundedRangeError
139 * - BitStringError
140 * - BitStringOverflow
141 * - InvalidBitStringRepresentation
142 * - RuntimeError
143 * - ConnectionError
144 * - ClusterUnavailable
145 * - CommandError
146 * - ConnectionFailed
147 * - ServerConnectionError (contains a message from server)
148 * - ConnectionTimeoutError
149 * - ConnectionBusy
150 * - ConnectionInterrupted
151 * - PoolError
152 * - ClusterError
153 * - InvalidConfig
154 * - InvalidDSN
155 *
156 *
157 * ----------
158 *
159 * @htmlonly <div class="bottom-nav"> @endhtmlonly
160 * ⇦ @ref pg_user_row_types | @ref pg_topology ⇨
161 * @htmlonly </div> @endhtmlonly
162 */
163
164//@{
165/** @name Generic driver errors */
166
167/// @brief Base class for all exceptions that may be thrown by the driver.
168class Error : public std::runtime_error {
169 using runtime_error::runtime_error;
170};
171
172/// @brief Base Postgres logic error.
173/// Reports errors that are consequences of erroneous driver usage,
174/// such as invalid query syntax, absence of appropriate parsers, out of range
175/// errors etc.
176/// These can be avoided by fixing code.
177class LogicError : public Error {
178 using Error::Error;
179};
180
181/// @brief Base Postgres runtime error.
182/// Reports errors that are consequences of erroneous data, misconfiguration,
183/// network errors etc.
184class RuntimeError : public Error {
185 using Error::Error;
186};
187
188/// @brief Error that was reported by PosgtreSQL server
189/// Contains the message sent by the server.
190/// Templated class because the errors can be both runtime and logic.
191template <typename Base>
192class ServerError : public Base {
193public:
194 explicit ServerError(const Message& msg) : Base(msg.GetMessage()), msg_{msg} {}
195
196 const Message& GetServerMessage() const { return msg_; }
197
198 Message::Severity GetSeverity() const { return msg_.GetSeverity(); }
199 SqlState GetSqlState() const { return msg_.GetSqlState(); }
200
201private:
202 Message msg_;
203};
204
205using ServerLogicError = ServerError<LogicError>;
206using ServerRuntimeError = ServerError<RuntimeError>;
207//@}
208
209//@{
210/** @name Connection errors */
212 using RuntimeError::RuntimeError;
213};
214
215/// @brief Exception is thrown when a single connection fails to connect
217public:
218 explicit ConnectionFailed(const Dsn& dsn);
219 ConnectionFailed(const Dsn& dsn, std::string_view message);
220};
221
222/// @brief Connection error reported by PostgreSQL server.
223/// Doc: https://www.postgresql.org/docs/12/static/errcodes-appendix.html
224/// Class 08 - Connection exception
227};
228
229/// @brief Indicates errors during pool operation
230class PoolError : public RuntimeError {
231public:
232 PoolError(std::string_view msg, std::string_view db_name);
233 PoolError(std::string_view msg);
234};
235
237 using ConnectionError::ConnectionError;
238};
239
240/// @brief Error when invoking a libpq function
242 using ConnectionError::ConnectionError;
243};
244
245/// @brief A network operation on a connection has timed out
247 using ConnectionError::ConnectionError;
248};
249
251 using RuntimeError::RuntimeError;
252};
253
254/// @brief An attempt to make a query to server was made while there is another
255/// query in flight.
257 using RuntimeError::RuntimeError;
258};
259
260/// @brief A network operation was interrupted by task cancellation.
262 using RuntimeError::RuntimeError;
263};
264
265//@}
266
267//@{
268/** @name SQL errors */
269//@{
270/** @name Class 03 — SQL Statement Not Yet Complete */
271/// A programming error, a statement is sent before other statement's results
272/// are processed.
275};
276//@}
277
278//@{
279/** @name Class 09 — Triggered Action Exception */
282};
283//@}
284
285//@{
286/** @name Class 0A — Feature Not Supported */
289};
290//@}
291
292//@{
293/** @name Class 0F - Locator Exception */
296};
297//@}
298
299//@{
300/** @name Class 0L - Invalid Grantor */
303};
304//@}
305
306//@{
307/** @name Class 0P - Invalid Role Specification */
310};
311//@}
312
313//@{
314/** @name Class 0Z - Diagnostics Exception */
317};
318//@}
319
320//@{
321/** @name Class 21 - Cardinality Violation */
324};
325//@}
326
327//@{
328/** @name Class 22 — Data Exception */
329/// @brief Base class for data exceptions
330/// Doc: https://www.postgresql.org/docs/12/static/errcodes-appendix.html
333};
334//@}
335
336//@{
337/** @name Class 23 — Integrity Constraint Violation */
338// TODO Shortcut accessors to respective message fields
339/// @brief Base class for integrity constraint violation errors.
340/// Doc: https://www.postgresql.org/docs/12/static/errcodes-appendix.html
342public:
344
345 std::string GetSchema() const;
346 std::string GetTable() const;
347 std::string GetConstraint() const;
348};
349
351 using IntegrityConstraintViolation::IntegrityConstraintViolation;
352};
353
355 using IntegrityConstraintViolation::IntegrityConstraintViolation;
356};
357
359 using IntegrityConstraintViolation::IntegrityConstraintViolation;
360};
361
363 using IntegrityConstraintViolation::IntegrityConstraintViolation;
364};
365
367 using IntegrityConstraintViolation::IntegrityConstraintViolation;
368};
369
371 using IntegrityConstraintViolation::IntegrityConstraintViolation;
372};
373
374/// Class 27 - Triggered Data Change Violation
376 using IntegrityConstraintViolation::IntegrityConstraintViolation;
377};
378
379/// Class 44 - WITH CHECK OPTION Violation
381 using IntegrityConstraintViolation::IntegrityConstraintViolation;
382};
383//@}
384
385//@{
386/** @name Class 24 - Invalid Cursor State */
389};
390//@}
391
392//@{
393/** @name Class 25 — Invalid Transaction State */
396};
397//@}
398
399//@{
400/** @name Class 26 - Invalid SQL Statement Name */
401/// This exception is thrown in case a prepared statement doesn't exist
404};
405//@}
406
407//@{
408/** @name Invalid object name, several classes */
409/// @brief Exception class for several Invalid * Name classes.
410/// Class 34 - Invalid Cursor Name
411/// Class 3D - Invalid Catalogue Name
412/// Class 3F - Invalid Schema Name
413/// TODO Add documentation (links) on the error classes
414/// TODO Split exception classes if needed based on documentation
417};
418//@}
419
420//@{
421/** @name Class 28 - Invalid Authorisation Specification */
424};
425//@}
426
427//@{
428/** @name Class 2B - Dependent Privilege Descriptors Still Exist */
431};
432//@}
433
434//@{
435/** @name Class 2D - Invalid Transaction Termination */
438};
439//@}
440
441//@{
442/** @name Class 38 - External Routine Exception */
445};
446//@}
447
448//@{
449/** @name Class 39 - External Routine Invocation Exception */
452};
453//@}
454
455//@{
456/** @name Class 3B - Savepoint Exception */
459};
460//@}
461
462//@{
463/** @name Class 2F — SQL Routine Exception */
466};
467//@}
468
469//@{
470/** @name Class 40 — Transaction Rollback */
473};
474//@}
475
476//@{
477/** @name Class 42 — Syntax Error or Access Rule Violation */
480};
481
484};
485
488};
489
490//@}
491
492//@{
493/** @name Class 53 - Insufficient Resources */
496};
497//@}
498
499//@{
500/** @name Class 54 - Program Limit Exceeded */
503};
504//@}
505
506//@{
507/** @name Class 55 - Object Not In Prerequisite State */
510};
511//@}
512
513//@{
514/** @name Class 57 - Operator Intervention */
517};
518
520 using OperatorIntervention::OperatorIntervention;
521};
522
524 using OperatorIntervention::OperatorIntervention;
525};
526
528 using OperatorIntervention::OperatorIntervention;
529};
530
532 using OperatorIntervention::OperatorIntervention;
533};
534
536 using OperatorIntervention::OperatorIntervention;
537};
538//@}
539
540//@{
541/** @name Class 58 - System Error (errors external to PostgreSQL itself) */
544};
545//@}
546
547//@{
548/** @name Class 72 — Snapshot Failure */
551};
552//@}
553
554//@{
555/** @name Class F0 — Configuration File Error */
558};
559//@}
560
561//@{
562/** @name Class HV — Foreign Data Wrapper Error (SQL/MED) */
565};
566//@}
567
568//@{
569/** @name Class P0 — PL/pgSQL Error */
572};
573//@}
574
575//@{
576/** @name Class XX — Internal Error */
579};
580//@}
581//@}
582
583//@{
584/** @name Transaction errors */
586 using LogicError::LogicError;
587};
588
590public:
591 AlreadyInTransaction();
592};
593
595public:
596 NotInTransaction();
597 NotInTransaction(const std::string& msg);
598};
599
601public:
602 TransactionForceRollback();
603 TransactionForceRollback(const std::string& msg);
604};
605
606//@}
607
608//@{
609/** @name Result set usage errors */
610
612public:
613 ResultSetError(std::string msg);
614
615 void AddMsgSuffix(std::string_view str);
616
617 const char* what() const noexcept override;
618
619private:
620 std::string msg_;
621};
622
623/// @brief Result set has less rows than the requested row index.
625public:
626 RowIndexOutOfBounds(std::size_t index);
627};
628
629/// @brief Result set has less columns that the requested index.
631public:
632 FieldIndexOutOfBounds(std::size_t index);
633};
634
635/// @brief Result set doesn't have field with the requested name.
637public:
638 FieldNameDoesntExist(std::string_view name);
639};
640
641/// @brief Data extraction from a null field value to a non-nullable type
642/// requested.
644public:
645 template <typename T>
646 FieldValueIsNull(std::size_t field_index, std::string_view field_name, const T&)
647 : ResultSetError(fmt::format(
648 "Field #{} name `{}` C++ type `{}` value is "
649 "null, forgot `std::optional`?",
650 field_index,
651 field_name,
652 compiler::GetTypeName<T>()
653 )) {}
654};
655
656/// @brief A value of a non-nullable type requested to be set null.
657/// Can occur if io::traits::IsNullable for the type is specialised as
658/// true_type, but io::traits::GetSetNull is not specialized appropriately.
660public:
661 TypeCannotBeNull(std::string_view type);
662};
663
664/// @brief Field buffer contains different category of data than expected by
665/// data parser.
667public:
668 InvalidParserCategory(std::string_view type, io::BufferCategory parser, io::BufferCategory buffer);
669};
670
671/// @brief While checking result set types, failed to determine the buffer
672/// category for a type oid.
673/// The context string is formed by the ResultSet and will have the form
674/// of 'result set field `foo` type `my_schema.bar` field `baz` array element'
676public:
677 UnknownBufferCategory(std::string_view context, Oid type_oid);
678
679 const Oid type_oid;
680};
681
682/// @brief A field in a result set doesn't have a binary parser.
684 using ResultSetError::ResultSetError;
685};
686
687/// @brief Buffer size is invalid for a fixed-size type.
688/// Can occur when a wrong field type is requested for reply.
690public:
691 InvalidInputBufferSize(std::size_t size, std::string_view message);
692};
693
694/// @brief Binary buffer contains invalid data.
695/// Can occur when parsing binary buffers containing multiple fields.
697public:
698 InvalidBinaryBuffer(const std::string& message);
699};
700
701/// @brief A tuple was requested to be parsed out of a row that doesn't have
702/// enough fields.
704public:
705 InvalidTupleSizeRequested(std::size_t field_count, std::size_t tuple_size);
706};
707
708/// @brief A row or result set requested to be treated as a single column, but
709/// contains more than one column.
711public:
712 NonSingleColumnResultSet(std::size_t actual_size, const std::string& type_name, const std::string& func);
713};
714
715/// @brief A result set containing a single row was expected
717public:
718 explicit NonSingleRowResultSet(std::size_t actual_size);
719};
720
721/// @brief A row was requested to be parsed based on field names/indexed,
722/// the count of names/indexes doesn't match the tuple size.
724public:
725 FieldTupleMismatch(std::size_t field_count, std::size_t tuple_size);
726};
727
728//@}
729
730//@{
731/// @brief Base error when working with mapped types
732class UserTypeError : public LogicError {
733 using LogicError::LogicError;
734};
735
736/// @brief PostgreSQL composite type has different count of members from
737/// the C++ counterpart.
739public:
740 CompositeSizeMismatch(std::size_t pg_size, std::size_t cpp_size, std::string_view cpp_type);
741};
742
743/// @brief PostgreSQL composite type has different member type that the C++
744/// mapping suggests.
746public:
747 CompositeMemberTypeMismatch(
748 std::string_view pg_type_schema,
749 std::string_view pg_type_name,
750 std::string_view field_name,
751 Oid pg_oid,
752 Oid user_oid
753 );
754};
755
756//@}
757
758//@{
759/** @name Array errors */
760/// @brief Base error when working with array types.
761class ArrayError : public LogicError {
762 using LogicError::LogicError;
763};
764
765/// @brief Array received from postgres has different dimensions from those of
766/// C++ container.
768public:
769 DimensionMismatch();
770};
771
773public:
774 InvalidDimensions(std::size_t expected, std::size_t actual);
775};
776
777//@}
778
779//@{
780/** @name Numeric/decimal datatype errors */
781class NumericError : public LogicError {
782 using LogicError::LogicError;
783};
784
785/// Value in PostgreSQL binary buffer cannot be represented by a given C++ type
787 using NumericError::NumericError;
788};
789
790/// PostgreSQL binary buffer contains NaN value, but the given C++ type doesn't
791/// support NaN value
792class ValueIsNaN : public NumericError {
793 using NumericError::NumericError;
794};
795
796/// Integral representation for a numeric contains invalid data
798 using NumericError::NumericError;
799};
800//@}
801
802/// @brief Invalid format for input data.
803///
804/// Can occur when a numeric string representation cannot be parsed for sending
805/// in binary buffers
807 using LogicError::LogicError;
808};
809
810//@{
811/** @name Enumeration type errors */
813 using LogicError::LogicError;
814};
815
817public:
818 InvalidEnumerationLiteral(std::string_view type_name, std::string_view literal);
819};
820
822public:
823 template <typename Enum>
824 explicit InvalidEnumerationValue(Enum val)
825 : EnumerationError(fmt::format(
826 "Invalid enumeration value '{}' for enum type '{}'",
827 USERVER_NAMESPACE::utils::UnderlyingValue(val),
828 compiler::GetTypeName<Enum>()
829 )) {}
830};
831//@}
832
833/// PostgreSQL interval datatype contains months field, which cannot be
834/// converted to microseconds unambiguously
836public:
837 UnsupportedInterval();
838};
839
840/// PostgreSQL range type has at least one end unbound
842public:
843 BoundedRangeError(std::string_view message);
844};
845
846//@{
847/** @name bit/bit varying type errors */
848
849/// @brief Base error when working with bit string types.
851public:
852 using LogicError::LogicError;
853};
854
855/// Value in PostgreSQL binary buffer cannot be represented by a given C++ type
857public:
858 BitStringOverflow(std::size_t actual, std::size_t expected);
859};
860
861/// Value in PostgreSQL binary buffer cannot be represented as bit string type
863public:
864 InvalidBitStringRepresentation();
865};
866//@}
867
868//@{
869/** @name Misc exceptions */
870class InvalidDSN : public RuntimeError {
871public:
872 InvalidDSN(std::string_view dsn, std::string_view err);
873};
874
876 using RuntimeError::RuntimeError;
877};
878
880 using LogicError::LogicError;
881};
882
883//@}
884
885//@{
886/** @name ip type errors */
888public:
889 using LogicError::LogicError;
890};
891
893public:
894 explicit IpAddressInvalidFormat(std::string_view str);
895};
896//@}
897
898} // namespace storages::postgres
899
900USERVER_NAMESPACE_END