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 (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