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