userver: userver/storages/postgres/exceptions.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 (TODO Decide if this is actually a runtime error)
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 {
193 public:
194 explicit ServerError(const Message& msg)
195 : Base(msg.GetMessage()), msg_{msg} {}
196
197 const Message& GetServerMessage() const { return msg_; }
198
199 Message::Severity GetSeverity() const { return msg_.GetSeverity(); }
200 SqlState GetSqlState() const { return msg_.GetSqlState(); }
201
202 private:
203 Message msg_;
204};
205
206using ServerLogicError = ServerError<LogicError>;
207using ServerRuntimeError = ServerError<RuntimeError>;
208//@}
209
210//@{
211/** @name Connection errors */
213 using RuntimeError::RuntimeError;
214};
215
216/// @brief Exception is thrown when a single connection fails to connect
218 public:
219 explicit ConnectionFailed(const Dsn& dsn);
220 ConnectionFailed(const Dsn& dsn, std::string_view message);
221};
222
223/// @brief Connection error reported by PostgreSQL server.
224/// Doc: https://www.postgresql.org/docs/12/static/errcodes-appendix.html
225/// Class 08 - Connection exception
228};
229
230/// @brief Indicates errors during pool operation
231class PoolError : public RuntimeError {
232 public:
233 PoolError(std::string_view msg, std::string_view db_name);
234 PoolError(std::string_view msg);
235};
236
238 using ConnectionError::ConnectionError;
239};
240
241/// @brief Error when invoking a libpq function
243 using ConnectionError::ConnectionError;
244};
245
246/// @brief A network operation on a connection has timed out
248 using ConnectionError::ConnectionError;
249};
250
252 using RuntimeError::RuntimeError;
253};
254
255/// @brief An attempt to make a query to server was made while there is another
256/// query in flight.
258 using RuntimeError::RuntimeError;
259};
260
261/// @brief A network operation was interrupted by task cancellation.
263 using RuntimeError::RuntimeError;
264};
265
266//@}
267
268//@{
269/** @name SQL errors */
270//@{
271/** @name Class 03 — SQL Statement Not Yet Complete */
272/// A programming error, a statement is sent before other statement's results
273/// are processed.
276};
277//@}
278
279//@{
280/** @name Class 09 — Triggered Action Exception */
283};
284//@}
285
286//@{
287/** @name Class 0A — Feature Not Supported */
290};
291//@}
292
293//@{
294/** @name Class 0F - Locator Exception */
297};
298//@}
299
300//@{
301/** @name Class 0L - Invalid Grantor */
304};
305//@}
306
307//@{
308/** @name Class 0P - Invalid Role Specification */
311};
312//@}
313
314//@{
315/** @name Class 0Z - Diagnostics Exception */
318};
319//@}
320
321//@{
322/** @name Class 21 - Cardinality Violation */
325};
326//@}
327
328//@{
329/** @name Class 22 — Data Exception */
330/// @brief Base class for data exceptions
331/// Doc: https://www.postgresql.org/docs/12/static/errcodes-appendix.html
334};
335//@}
336
337//@{
338/** @name Class 23 — Integrity Constraint Violation */
339// TODO Shortcut accessors to respective message fields
340/// @brief Base class for integrity constraint violation errors.
341/// Doc: https://www.postgresql.org/docs/12/static/errcodes-appendix.html
343 public:
345
346 std::string GetSchema() const;
347 std::string GetTable() const;
348 std::string GetConstraint() const;
349};
350
352 using IntegrityConstraintViolation::IntegrityConstraintViolation;
353};
354
356 using IntegrityConstraintViolation::IntegrityConstraintViolation;
357};
358
360 using IntegrityConstraintViolation::IntegrityConstraintViolation;
361};
362
364 using IntegrityConstraintViolation::IntegrityConstraintViolation;
365};
366
368 using IntegrityConstraintViolation::IntegrityConstraintViolation;
369};
370
372 using IntegrityConstraintViolation::IntegrityConstraintViolation;
373};
374
375/// Class 27 - Triggered Data Change Violation
377 using IntegrityConstraintViolation::IntegrityConstraintViolation;
378};
379
380/// Class 44 - WITH CHECK OPTION Violation
382 using IntegrityConstraintViolation::IntegrityConstraintViolation;
383};
384//@}
385
386//@{
387/** @name Class 24 - Invalid Cursor State */
390};
391//@}
392
393//@{
394/** @name Class 25 — Invalid Transaction State */
397};
398//@}
399
400//@{
401/** @name Class 26 - Invalid SQL Statement Name */
402/// This exception is thrown in case a prepared statement doesn't exist
405};
406//@}
407
408//@{
409/** @name Invalid object name, several classes */
410/// @brief Exception class for several Invalid * Name classes.
411/// Class 34 - Invalid Cursor Name
412/// Class 3D - Invalid Catalogue Name
413/// Class 3F - Invalid Schema Name
414/// TODO Add documentation (links) on the error classes
415/// TODO Split exception classes if needed based on documentation
418};
419//@}
420
421//@{
422/** @name Class 28 - Invalid Authorisation Specification */
425};
426//@}
427
428//@{
429/** @name Class 2B - Dependent Privilege Descriptors Still Exist */
432};
433//@}
434
435//@{
436/** @name Class 2D - Invalid Transaction Termination */
439};
440//@}
441
442//@{
443/** @name Class 38 - External Routine Exception */
446};
447//@}
448
449//@{
450/** @name Class 39 - External Routine Invocation Exception */
453};
454//@}
455
456//@{
457/** @name Class 3B - Savepoint Exception */
460};
461//@}
462
463//@{
464/** @name Class 2F — SQL Routine Exception */
467};
468//@}
469
470//@{
471/** @name Class 40 — Transaction Rollback */
474};
475//@}
476
477//@{
478/** @name Class 42 — Syntax Error or Access Rule Violation */
481};
482
485};
486
489};
490
491//@}
492
493//@{
494/** @name Class 53 - Insufficient Resources */
497};
498//@}
499
500//@{
501/** @name Class 54 - Program Limit Exceeded */
504};
505//@}
506
507//@{
508/** @name Class 55 - Object Not In Prerequisite State */
511};
512//@}
513
514//@{
515/** @name Class 57 - Operator Intervention */
518};
519
521 using OperatorIntervention::OperatorIntervention;
522};
523
525 using OperatorIntervention::OperatorIntervention;
526};
527
529 using OperatorIntervention::OperatorIntervention;
530};
531
533 using OperatorIntervention::OperatorIntervention;
534};
535
537 using OperatorIntervention::OperatorIntervention;
538};
539//@}
540
541//@{
542/** @name Class 58 - System Error (errors external to PostgreSQL itself) */
545};
546//@}
547
548//@{
549/** @name Class 72 — Snapshot Failure */
552};
553//@}
554
555//@{
556/** @name Class F0 — Configuration File Error */
559};
560//@}
561
562//@{
563/** @name Class HV — Foreign Data Wrapper Error (SQL/MED) */
566};
567//@}
568
569//@{
570/** @name Class P0 — PL/pgSQL Error */
573};
574//@}
575
576//@{
577/** @name Class XX — Internal Error */
580};
581//@}
582//@}
583
584//@{
585/** @name Transaction errors */
587 using LogicError::LogicError;
588};
589
591 public:
592 AlreadyInTransaction();
593};
594
596 public:
597 NotInTransaction();
598 NotInTransaction(const std::string& msg);
599};
600
602 public:
603 TransactionForceRollback();
604 TransactionForceRollback(const std::string& msg);
605};
606
607//@}
608
609//@{
610/** @name Result set usage errors */
611
613 public:
614 ResultSetError(std::string msg);
615
616 void AddMsgSuffix(std::string_view str);
617
618 const char* what() const noexcept override;
619
620 private:
621 std::string msg_;
622};
623
624/// @brief Result set has less rows than the requested row index.
626 public:
627 RowIndexOutOfBounds(std::size_t index);
628};
629
630/// @brief Result set has less columns that the requested index.
632 public:
633 FieldIndexOutOfBounds(std::size_t index);
634};
635
636/// @brief Result set doesn't have field with the requested name.
638 public:
639 FieldNameDoesntExist(std::string_view name);
640};
641
642/// @brief Data extraction from a null field value to a non-nullable type
643/// requested.
645 public:
646 template <typename T>
647 FieldValueIsNull(std::size_t field_index, std::string_view field_name,
648 const T&)
649 : ResultSetError(fmt::format("Field #{} name `{}` C++ type `{}` value is "
650 "null, forgot `std::optional`?",
651 field_index, field_name,
652 compiler::GetTypeName<T>())) {}
653};
654
655/// @brief A value of a non-nullable type requested to be set null.
656/// Can occur if io::traits::IsNullable for the type is specialised as
657/// true_type, but io::traits::GetSetNull is not specialized appropriately.
659 public:
660 TypeCannotBeNull(std::string_view type);
661};
662
663/// @brief Field buffer contains different category of data than expected by
664/// data parser.
666 public:
667 InvalidParserCategory(std::string_view type, io::BufferCategory parser,
668 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'
676 public:
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.
690 public:
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.
697 public:
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.
704 public:
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.
711 public:
712 NonSingleColumnResultSet(std::size_t actual_size,
713 const std::string& type_name,
714 const std::string& func);
715};
716
717/// @brief A result set containing a single row was expected
719 public:
720 explicit NonSingleRowResultSet(std::size_t actual_size);
721};
722
723/// @brief A row was requested to be parsed based on field names/indexed,
724/// the count of names/indexes doesn't match the tuple size.
726 public:
727 FieldTupleMismatch(std::size_t field_count, std::size_t tuple_size);
728};
729
730//@}
731
732//@{
733/// @brief Base error when working with mapped types
734class UserTypeError : public LogicError {
735 using LogicError::LogicError;
736};
737
738/// @brief PostgreSQL composite type has different count of members from
739/// the C++ counterpart.
741 public:
742 CompositeSizeMismatch(std::size_t pg_size, std::size_t cpp_size,
743 std::string_view cpp_type);
744};
745
746/// @brief PostgreSQL composite type has different member type that the C++
747/// mapping suggests.
749 public:
750 CompositeMemberTypeMismatch(std::string_view pg_type_schema,
751 std::string_view pg_type_name,
752 std::string_view field_name, Oid pg_oid,
753 Oid user_oid);
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.
768 public:
769 DimensionMismatch();
770};
771
773 public:
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
817 public:
818 InvalidEnumerationLiteral(std::string_view type_name,
819 std::string_view literal);
820};
821
823 public:
824 template <typename Enum>
825 explicit InvalidEnumerationValue(Enum val)
826 : EnumerationError(
827 fmt::format("Invalid enumeration value '{}' for enum type '{}'",
828 USERVER_NAMESPACE::utils::UnderlyingValue(val),
829 compiler::GetTypeName<Enum>())) {}
830};
831//@}
832
833/// PostgreSQL interval datatype contains months field, which cannot be
834/// converted to microseconds unambiguously
836 public:
837 UnsupportedInterval();
838};
839
840/// PostgreSQL range type has at least one end unbound
842 public:
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.
851 public:
852 using LogicError::LogicError;
853};
854
855/// Value in PostgreSQL binary buffer cannot be represented by a given C++ type
857 public:
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
863 public:
864 InvalidBitStringRepresentation();
865};
866//@}
867
868//@{
869/** @name Misc exceptions */
870class InvalidDSN : public RuntimeError {
871 public:
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 */
888 public:
889 using LogicError::LogicError;
890};
891
893 public:
894 explicit IpAddressInvalidFormat(std::string_view str);
895};
896//@}
897
898} // namespace storages::postgres
899
900USERVER_NAMESPACE_END