]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Added errors module documentation
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 22 Nov 2020 04:32:01 +0000 (04:32 +0000)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 22 Nov 2020 04:49:29 +0000 (04:49 +0000)
docs/_static/psycopg.css
docs/connection.rst
docs/errors.rst [new file with mode: 0644]
docs/index.rst
psycopg3/psycopg3/errors.py
tools/update_errors.py

index 31629786585aa0446ed49033c65613e6076b0b7d..e4acd5b71a86a72c1522a83bce9f322ec6e82852 100644 (file)
@@ -2,3 +2,8 @@
   background: #ffc;
   border: 1px solid #dda;
 }
+
+/* more compact errors table */
+#sqlstate-exceptions table p {
+  margin: 0;
+}
index 451e6fca6de8608978ca1b66530f503eaa2edfb6..0f76c1b96d148e5938ed2a68a4719ce2ff545521 100644 (file)
@@ -110,9 +110,11 @@ The `!Connection` class
 
     .. automethod:: cancel
     .. automethod:: add_notice_handler
-    .. automethod:: remove_notice_handler
 
-    TODO: document `Diagnostic`
+        The argument of the callback is a `~psycopg3.errors.Diagnostic` object
+        containing all the details about the notice.
+
+    .. automethod:: remove_notice_handler
 
 
 The `!AsyncConnection` class
diff --git a/docs/errors.rst b/docs/errors.rst
new file mode 100644 (file)
index 0000000..436efa4
--- /dev/null
@@ -0,0 +1,479 @@
+`psycopg3.errors` -- package exceptions
+=======================================
+
+.. index::
+    single: Error; Class
+
+.. module:: psycopg3.errors
+
+This module exposes objects to represent and examine database errors.
+
+.. autoexception:: Error()
+
+    .. autoattribute:: diag
+
+.. autoclass:: Diagnostic()
+
+    The object is returned by the `Error.diag` attribute and is passed to the
+    callback functions registered with
+    `~psycopg3.Connection.add_notice_handler()`.
+
+    All the information available from the `PQresultErrorField()`__ function
+    are exposed as attributes by the object. For instance the `!severity`
+    attribute returns the `!PG_DIAG_SEVERITY` code. Please refer to the
+    PostgreSQL documentation for the meaning of all the attributes.
+
+    .. __: https://www.postgresql.org/docs/current/static/libpq-exec.html
+        #LIBPQ-PQRESULTERRORFIELD
+
+    The attributes available are:
+
+    .. attribute::
+        column_name
+        constraint_name
+        context
+        datatype_name
+        internal_position
+        internal_query
+        message_detail
+        message_hint
+        message_primary
+        schema_name
+        severity
+        severity_nonlocalized
+        source_file
+        source_function
+        source_line
+        sqlstate
+        statement_position
+        table_name
+
+        A string with the error field if available; `!None` if not available.
+        The attribute value is available only for errors sent by the server:
+        not all the fields are available for all the errors and for all the
+        server versions.
+
+
+.. index::
+    single: Exceptions; DB API
+
+DBAPI exceptions
+----------------
+
+In compliance with the DB API, all the exceptions raised by ``psycopg3``
+derive from the following classes:
+
+.. parsed-literal::
+
+    `!Exception`
+    \|__ `Warning`
+    \|__ `Error`
+        \|__ `InterfaceError`
+        \|__ `DatabaseError`
+            \|__ `DataError`
+            \|__ `OperationalError`
+            \|__ `IntegrityError`
+            \|__ `InternalError`
+            \|__ `ProgrammingError`
+            \|__ `NotSupportedError`
+
+These classes are also exposed by the ``psycopg3`` module.
+
+.. autoexception:: Warning()
+.. autoexception:: InterfaceError()
+.. autoexception:: DatabaseError()
+.. autoexception:: DataError()
+.. autoexception:: OperationalError()
+.. autoexception:: IntegrityError()
+.. autoexception:: InternalError()
+.. autoexception:: ProgrammingError()
+.. autoexception:: NotSupportedError()
+
+
+.. index::
+    single: Exceptions; PostgreSQL
+
+SQLSTATE exceptions
+-------------------
+
+Errors coming from a database server (as opposite as ones generated
+client-side, such as connection failed) usually have a 5-letters error code
+called SQLSTATE (available in the ``diag.sqlstate`` attribute).
+
+``psycopg3`` exposes a different class for each SQLSTATE value, allowing to
+write idiomatic error handling code according to specific conditions happening
+in the database:
+
+.. code-block:: python
+
+    try:
+        cur.execute("LOCK TABLE mytable IN ACCESS EXCLUSIVE MODE NOWAIT")
+    except psycopg3.errors.LockNotAvailable:
+        locked = True
+
+The exception names are generated from the PostgreSQL source code and includes
+classes for every error defined by PostgreSQL in versions between 9.6 and 13.
+Every class in the module is named after what referred as "condition name" `in
+the documentation`__, converted to CamelCase: e.g. the error 22012,
+``division_by_zero`` is exposed by this module as the class `!DivisionByZero`.
+There is a handful of exception, required for disambiguate clashes: please
+refer to the table below for all the classes defined.
+
+.. __: https://www.postgresql.org/docs/current/static/errcodes-appendix.html#ERRCODES-TABLE
+
+Every exception class is a subclass of one of the :ref:`standard DB-API
+exception <dbapi-exceptions>` and expose the `~Error` interface.
+
+
+.. autofunction:: lookup
+
+    Example:
+
+    .. code-block:: python
+
+        try:
+            cur.execute("LOCK TABLE mytable IN ACCESS EXCLUSIVE MODE NOWAIT")
+        except psycopg3.errors.lookup("55P03"):
+            locked = True
+
+These are all the classes defined and the DBAPI exception they derive from:
+
+.. autogenerated: start
+
+========= ================================================== ====================
+SQLSTATE  Exception                                          Base exception
+========= ================================================== ====================
+**Class 02**: Class 02 - No Data (this is also a warning class per the SQL standard)
+---------------------------------------------------------------------------------
+``02000`` `!NoData`                                          `!DatabaseError`
+``02001`` `!NoAdditionalDynamicResultSetsReturned`           `!DatabaseError`
+**Class 03**: Class 03 - SQL Statement Not Yet Complete
+---------------------------------------------------------------------------------
+``03000`` `!SqlStatementNotYetComplete`                      `!DatabaseError`
+**Class 08**: Class 08 - Connection Exception
+---------------------------------------------------------------------------------
+``08000`` `!ConnectionException`                             `!OperationalError`
+``08001`` `!SqlclientUnableToEstablishSqlconnection`         `!OperationalError`
+``08003`` `!ConnectionDoesNotExist`                          `!OperationalError`
+``08004`` `!SqlserverRejectedEstablishmentOfSqlconnection`   `!OperationalError`
+``08006`` `!ConnectionFailure`                               `!OperationalError`
+``08007`` `!TransactionResolutionUnknown`                    `!OperationalError`
+``08P01`` `!ProtocolViolation`                               `!OperationalError`
+**Class 09**: Class 09 - Triggered Action Exception
+---------------------------------------------------------------------------------
+``09000`` `!TriggeredActionException`                        `!DatabaseError`
+**Class 0A**: Class 0A - Feature Not Supported
+---------------------------------------------------------------------------------
+``0A000`` `!FeatureNotSupported`                             `!NotSupportedError`
+**Class 0B**: Class 0B - Invalid Transaction Initiation
+---------------------------------------------------------------------------------
+``0B000`` `!InvalidTransactionInitiation`                    `!DatabaseError`
+**Class 0F**: Class 0F - Locator Exception
+---------------------------------------------------------------------------------
+``0F000`` `!LocatorException`                                `!DatabaseError`
+``0F001`` `!InvalidLocatorSpecification`                     `!DatabaseError`
+**Class 0L**: Class 0L - Invalid Grantor
+---------------------------------------------------------------------------------
+``0L000`` `!InvalidGrantor`                                  `!DatabaseError`
+``0LP01`` `!InvalidGrantOperation`                           `!DatabaseError`
+**Class 0P**: Class 0P - Invalid Role Specification
+---------------------------------------------------------------------------------
+``0P000`` `!InvalidRoleSpecification`                        `!DatabaseError`
+**Class 0Z**: Class 0Z - Diagnostics Exception
+---------------------------------------------------------------------------------
+``0Z000`` `!DiagnosticsException`                            `!DatabaseError`
+``0Z002`` `!StackedDiagnosticsAccessedWithoutActiveHandler`  `!DatabaseError`
+**Class 20**: Class 20 - Case Not Found
+---------------------------------------------------------------------------------
+``20000`` `!CaseNotFound`                                    `!ProgrammingError`
+**Class 21**: Class 21 - Cardinality Violation
+---------------------------------------------------------------------------------
+``21000`` `!CardinalityViolation`                            `!ProgrammingError`
+**Class 22**: Class 22 - Data Exception
+---------------------------------------------------------------------------------
+``22000`` `!DataException`                                   `!DataError`
+``22001`` `!StringDataRightTruncation`                       `!DataError`
+``22002`` `!NullValueNoIndicatorParameter`                   `!DataError`
+``22003`` `!NumericValueOutOfRange`                          `!DataError`
+``22004`` `!NullValueNotAllowed`                             `!DataError`
+``22005`` `!ErrorInAssignment`                               `!DataError`
+``22007`` `!InvalidDatetimeFormat`                           `!DataError`
+``22008`` `!DatetimeFieldOverflow`                           `!DataError`
+``22009`` `!InvalidTimeZoneDisplacementValue`                `!DataError`
+``2200B`` `!EscapeCharacterConflict`                         `!DataError`
+``2200C`` `!InvalidUseOfEscapeCharacter`                     `!DataError`
+``2200D`` `!InvalidEscapeOctet`                              `!DataError`
+``2200F`` `!ZeroLengthCharacterString`                       `!DataError`
+``2200G`` `!MostSpecificTypeMismatch`                        `!DataError`
+``2200H`` `!SequenceGeneratorLimitExceeded`                  `!DataError`
+``2200L`` `!NotAnXmlDocument`                                `!DataError`
+``2200M`` `!InvalidXmlDocument`                              `!DataError`
+``2200N`` `!InvalidXmlContent`                               `!DataError`
+``2200S`` `!InvalidXmlComment`                               `!DataError`
+``2200T`` `!InvalidXmlProcessingInstruction`                 `!DataError`
+``22010`` `!InvalidIndicatorParameterValue`                  `!DataError`
+``22011`` `!SubstringError`                                  `!DataError`
+``22012`` `!DivisionByZero`                                  `!DataError`
+``22013`` `!InvalidPrecedingOrFollowingSize`                 `!DataError`
+``22014`` `!InvalidArgumentForNtileFunction`                 `!DataError`
+``22015`` `!IntervalFieldOverflow`                           `!DataError`
+``22016`` `!InvalidArgumentForNthValueFunction`              `!DataError`
+``22018`` `!InvalidCharacterValueForCast`                    `!DataError`
+``22019`` `!InvalidEscapeCharacter`                          `!DataError`
+``2201B`` `!InvalidRegularExpression`                        `!DataError`
+``2201E`` `!InvalidArgumentForLogarithm`                     `!DataError`
+``2201F`` `!InvalidArgumentForPowerFunction`                 `!DataError`
+``2201G`` `!InvalidArgumentForWidthBucketFunction`           `!DataError`
+``2201W`` `!InvalidRowCountInLimitClause`                    `!DataError`
+``2201X`` `!InvalidRowCountInResultOffsetClause`             `!DataError`
+``22021`` `!CharacterNotInRepertoire`                        `!DataError`
+``22022`` `!IndicatorOverflow`                               `!DataError`
+``22023`` `!InvalidParameterValue`                           `!DataError`
+``22024`` `!UnterminatedCString`                             `!DataError`
+``22025`` `!InvalidEscapeSequence`                           `!DataError`
+``22026`` `!StringDataLengthMismatch`                        `!DataError`
+``22027`` `!TrimError`                                       `!DataError`
+``2202E`` `!ArraySubscriptError`                             `!DataError`
+``2202G`` `!InvalidTablesampleRepeat`                        `!DataError`
+``2202H`` `!InvalidTablesampleArgument`                      `!DataError`
+``22030`` `!DuplicateJsonObjectKeyValue`                     `!DataError`
+``22031`` `!InvalidArgumentForSqlJsonDatetimeFunction`       `!DataError`
+``22032`` `!InvalidJsonText`                                 `!DataError`
+``22033`` `!InvalidSqlJsonSubscript`                         `!DataError`
+``22034`` `!MoreThanOneSqlJsonItem`                          `!DataError`
+``22035`` `!NoSqlJsonItem`                                   `!DataError`
+``22036`` `!NonNumericSqlJsonItem`                           `!DataError`
+``22037`` `!NonUniqueKeysInAJsonObject`                      `!DataError`
+``22038`` `!SingletonSqlJsonItemRequired`                    `!DataError`
+``22039`` `!SqlJsonArrayNotFound`                            `!DataError`
+``2203A`` `!SqlJsonMemberNotFound`                           `!DataError`
+``2203B`` `!SqlJsonNumberNotFound`                           `!DataError`
+``2203C`` `!SqlJsonObjectNotFound`                           `!DataError`
+``2203D`` `!TooManyJsonArrayElements`                        `!DataError`
+``2203E`` `!TooManyJsonObjectMembers`                        `!DataError`
+``2203F`` `!SqlJsonScalarRequired`                           `!DataError`
+``22P01`` `!FloatingPointException`                          `!DataError`
+``22P02`` `!InvalidTextRepresentation`                       `!DataError`
+``22P03`` `!InvalidBinaryRepresentation`                     `!DataError`
+``22P04`` `!BadCopyFileFormat`                               `!DataError`
+``22P05`` `!UntranslatableCharacter`                         `!DataError`
+``22P06`` `!NonstandardUseOfEscapeCharacter`                 `!DataError`
+**Class 23**: Class 23 - Integrity Constraint Violation
+---------------------------------------------------------------------------------
+``23000`` `!IntegrityConstraintViolation`                    `!IntegrityError`
+``23001`` `!RestrictViolation`                               `!IntegrityError`
+``23502`` `!NotNullViolation`                                `!IntegrityError`
+``23503`` `!ForeignKeyViolation`                             `!IntegrityError`
+``23505`` `!UniqueViolation`                                 `!IntegrityError`
+``23514`` `!CheckViolation`                                  `!IntegrityError`
+``23P01`` `!ExclusionViolation`                              `!IntegrityError`
+**Class 24**: Class 24 - Invalid Cursor State
+---------------------------------------------------------------------------------
+``24000`` `!InvalidCursorState`                              `!InternalError`
+**Class 25**: Class 25 - Invalid Transaction State
+---------------------------------------------------------------------------------
+``25000`` `!InvalidTransactionState`                         `!InternalError`
+``25001`` `!ActiveSqlTransaction`                            `!InternalError`
+``25002`` `!BranchTransactionAlreadyActive`                  `!InternalError`
+``25003`` `!InappropriateAccessModeForBranchTransaction`     `!InternalError`
+``25004`` `!InappropriateIsolationLevelForBranchTransaction` `!InternalError`
+``25005`` `!NoActiveSqlTransactionForBranchTransaction`      `!InternalError`
+``25006`` `!ReadOnlySqlTransaction`                          `!InternalError`
+``25007`` `!SchemaAndDataStatementMixingNotSupported`        `!InternalError`
+``25008`` `!HeldCursorRequiresSameIsolationLevel`            `!InternalError`
+``25P01`` `!NoActiveSqlTransaction`                          `!InternalError`
+``25P02`` `!InFailedSqlTransaction`                          `!InternalError`
+``25P03`` `!IdleInTransactionSessionTimeout`                 `!InternalError`
+**Class 26**: Class 26 - Invalid SQL Statement Name
+---------------------------------------------------------------------------------
+``26000`` `!InvalidSqlStatementName`                         `!ProgrammingError`
+**Class 27**: Class 27 - Triggered Data Change Violation
+---------------------------------------------------------------------------------
+``27000`` `!TriggeredDataChangeViolation`                    `!OperationalError`
+**Class 28**: Class 28 - Invalid Authorization Specification
+---------------------------------------------------------------------------------
+``28000`` `!InvalidAuthorizationSpecification`               `!OperationalError`
+``28P01`` `!InvalidPassword`                                 `!OperationalError`
+**Class 2B**: Class 2B - Dependent Privilege Descriptors Still Exist
+---------------------------------------------------------------------------------
+``2B000`` `!DependentPrivilegeDescriptorsStillExist`         `!InternalError`
+``2BP01`` `!DependentObjectsStillExist`                      `!InternalError`
+**Class 2D**: Class 2D - Invalid Transaction Termination
+---------------------------------------------------------------------------------
+``2D000`` `!InvalidTransactionTermination`                   `!InternalError`
+**Class 2F**: Class 2F - SQL Routine Exception
+---------------------------------------------------------------------------------
+``2F000`` `!SqlRoutineException`                             `!OperationalError`
+``2F002`` `!ModifyingSqlDataNotPermitted`                    `!OperationalError`
+``2F003`` `!ProhibitedSqlStatementAttempted`                 `!OperationalError`
+``2F004`` `!ReadingSqlDataNotPermitted`                      `!OperationalError`
+``2F005`` `!FunctionExecutedNoReturnStatement`               `!OperationalError`
+**Class 34**: Class 34 - Invalid Cursor Name
+---------------------------------------------------------------------------------
+``34000`` `!InvalidCursorName`                               `!ProgrammingError`
+**Class 38**: Class 38 - External Routine Exception
+---------------------------------------------------------------------------------
+``38000`` `!ExternalRoutineException`                        `!OperationalError`
+``38001`` `!ContainingSqlNotPermitted`                       `!OperationalError`
+``38002`` `!ModifyingSqlDataNotPermittedExt`                 `!OperationalError`
+``38003`` `!ProhibitedSqlStatementAttemptedExt`              `!OperationalError`
+``38004`` `!ReadingSqlDataNotPermittedExt`                   `!OperationalError`
+**Class 39**: Class 39 - External Routine Invocation Exception
+---------------------------------------------------------------------------------
+``39000`` `!ExternalRoutineInvocationException`              `!OperationalError`
+``39001`` `!InvalidSqlstateReturned`                         `!OperationalError`
+``39004`` `!NullValueNotAllowedExt`                          `!OperationalError`
+``39P01`` `!TriggerProtocolViolated`                         `!OperationalError`
+``39P02`` `!SrfProtocolViolated`                             `!OperationalError`
+``39P03`` `!EventTriggerProtocolViolated`                    `!OperationalError`
+**Class 3B**: Class 3B - Savepoint Exception
+---------------------------------------------------------------------------------
+``3B000`` `!SavepointException`                              `!OperationalError`
+``3B001`` `!InvalidSavepointSpecification`                   `!OperationalError`
+**Class 3D**: Class 3D - Invalid Catalog Name
+---------------------------------------------------------------------------------
+``3D000`` `!InvalidCatalogName`                              `!ProgrammingError`
+**Class 3F**: Class 3F - Invalid Schema Name
+---------------------------------------------------------------------------------
+``3F000`` `!InvalidSchemaName`                               `!ProgrammingError`
+**Class 40**: Class 40 - Transaction Rollback
+---------------------------------------------------------------------------------
+``40000`` `!TransactionRollback`                             `!OperationalError`
+``40001`` `!SerializationFailure`                            `!OperationalError`
+``40002`` `!TransactionIntegrityConstraintViolation`         `!OperationalError`
+``40003`` `!StatementCompletionUnknown`                      `!OperationalError`
+``40P01`` `!DeadlockDetected`                                `!OperationalError`
+**Class 42**: Class 42 - Syntax Error or Access Rule Violation
+---------------------------------------------------------------------------------
+``42000`` `!SyntaxErrorOrAccessRuleViolation`                `!ProgrammingError`
+``42501`` `!InsufficientPrivilege`                           `!ProgrammingError`
+``42601`` `!SyntaxError`                                     `!ProgrammingError`
+``42602`` `!InvalidName`                                     `!ProgrammingError`
+``42611`` `!InvalidColumnDefinition`                         `!ProgrammingError`
+``42622`` `!NameTooLong`                                     `!ProgrammingError`
+``42701`` `!DuplicateColumn`                                 `!ProgrammingError`
+``42702`` `!AmbiguousColumn`                                 `!ProgrammingError`
+``42703`` `!UndefinedColumn`                                 `!ProgrammingError`
+``42704`` `!UndefinedObject`                                 `!ProgrammingError`
+``42710`` `!DuplicateObject`                                 `!ProgrammingError`
+``42712`` `!DuplicateAlias`                                  `!ProgrammingError`
+``42723`` `!DuplicateFunction`                               `!ProgrammingError`
+``42725`` `!AmbiguousFunction`                               `!ProgrammingError`
+``42803`` `!GroupingError`                                   `!ProgrammingError`
+``42804`` `!DatatypeMismatch`                                `!ProgrammingError`
+``42809`` `!WrongObjectType`                                 `!ProgrammingError`
+``42830`` `!InvalidForeignKey`                               `!ProgrammingError`
+``42846`` `!CannotCoerce`                                    `!ProgrammingError`
+``42883`` `!UndefinedFunction`                               `!ProgrammingError`
+``428C9`` `!GeneratedAlways`                                 `!ProgrammingError`
+``42939`` `!ReservedName`                                    `!ProgrammingError`
+``42P01`` `!UndefinedTable`                                  `!ProgrammingError`
+``42P02`` `!UndefinedParameter`                              `!ProgrammingError`
+``42P03`` `!DuplicateCursor`                                 `!ProgrammingError`
+``42P04`` `!DuplicateDatabase`                               `!ProgrammingError`
+``42P05`` `!DuplicatePreparedStatement`                      `!ProgrammingError`
+``42P06`` `!DuplicateSchema`                                 `!ProgrammingError`
+``42P07`` `!DuplicateTable`                                  `!ProgrammingError`
+``42P08`` `!AmbiguousParameter`                              `!ProgrammingError`
+``42P09`` `!AmbiguousAlias`                                  `!ProgrammingError`
+``42P10`` `!InvalidColumnReference`                          `!ProgrammingError`
+``42P11`` `!InvalidCursorDefinition`                         `!ProgrammingError`
+``42P12`` `!InvalidDatabaseDefinition`                       `!ProgrammingError`
+``42P13`` `!InvalidFunctionDefinition`                       `!ProgrammingError`
+``42P14`` `!InvalidPreparedStatementDefinition`              `!ProgrammingError`
+``42P15`` `!InvalidSchemaDefinition`                         `!ProgrammingError`
+``42P16`` `!InvalidTableDefinition`                          `!ProgrammingError`
+``42P17`` `!InvalidObjectDefinition`                         `!ProgrammingError`
+``42P18`` `!IndeterminateDatatype`                           `!ProgrammingError`
+``42P19`` `!InvalidRecursion`                                `!ProgrammingError`
+``42P20`` `!WindowingError`                                  `!ProgrammingError`
+``42P21`` `!CollationMismatch`                               `!ProgrammingError`
+``42P22`` `!IndeterminateCollation`                          `!ProgrammingError`
+**Class 44**: Class 44 - WITH CHECK OPTION Violation
+---------------------------------------------------------------------------------
+``44000`` `!WithCheckOptionViolation`                        `!ProgrammingError`
+**Class 53**: Class 53 - Insufficient Resources
+---------------------------------------------------------------------------------
+``53000`` `!InsufficientResources`                           `!OperationalError`
+``53100`` `!DiskFull`                                        `!OperationalError`
+``53200`` `!OutOfMemory`                                     `!OperationalError`
+``53300`` `!TooManyConnections`                              `!OperationalError`
+``53400`` `!ConfigurationLimitExceeded`                      `!OperationalError`
+**Class 54**: Class 54 - Program Limit Exceeded
+---------------------------------------------------------------------------------
+``54000`` `!ProgramLimitExceeded`                            `!OperationalError`
+``54001`` `!StatementTooComplex`                             `!OperationalError`
+``54011`` `!TooManyColumns`                                  `!OperationalError`
+``54023`` `!TooManyArguments`                                `!OperationalError`
+**Class 55**: Class 55 - Object Not In Prerequisite State
+---------------------------------------------------------------------------------
+``55000`` `!ObjectNotInPrerequisiteState`                    `!OperationalError`
+``55006`` `!ObjectInUse`                                     `!OperationalError`
+``55P02`` `!CantChangeRuntimeParam`                          `!OperationalError`
+``55P03`` `!LockNotAvailable`                                `!OperationalError`
+``55P04`` `!UnsafeNewEnumValueUsage`                         `!OperationalError`
+**Class 57**: Class 57 - Operator Intervention
+---------------------------------------------------------------------------------
+``57000`` `!OperatorIntervention`                            `!OperationalError`
+``57014`` `!QueryCanceled`                                   `!OperationalError`
+``57P01`` `!AdminShutdown`                                   `!OperationalError`
+``57P02`` `!CrashShutdown`                                   `!OperationalError`
+``57P03`` `!CannotConnectNow`                                `!OperationalError`
+``57P04`` `!DatabaseDropped`                                 `!OperationalError`
+**Class 58**: Class 58 - System Error (errors external to PostgreSQL itself)
+---------------------------------------------------------------------------------
+``58000`` `!SystemError`                                     `!OperationalError`
+``58030`` `!IoError`                                         `!OperationalError`
+``58P01`` `!UndefinedFile`                                   `!OperationalError`
+``58P02`` `!DuplicateFile`                                   `!OperationalError`
+**Class 72**: Class 72 - Snapshot Failure
+---------------------------------------------------------------------------------
+``72000`` `!SnapshotTooOld`                                  `!DatabaseError`
+**Class F0**: Class F0 - Configuration File Error
+---------------------------------------------------------------------------------
+``F0000`` `!ConfigFileError`                                 `!OperationalError`
+``F0001`` `!LockFileExists`                                  `!OperationalError`
+**Class HV**: Class HV - Foreign Data Wrapper Error (SQL/MED)
+---------------------------------------------------------------------------------
+``HV000`` `!FdwError`                                        `!OperationalError`
+``HV001`` `!FdwOutOfMemory`                                  `!OperationalError`
+``HV002`` `!FdwDynamicParameterValueNeeded`                  `!OperationalError`
+``HV004`` `!FdwInvalidDataType`                              `!OperationalError`
+``HV005`` `!FdwColumnNameNotFound`                           `!OperationalError`
+``HV006`` `!FdwInvalidDataTypeDescriptors`                   `!OperationalError`
+``HV007`` `!FdwInvalidColumnName`                            `!OperationalError`
+``HV008`` `!FdwInvalidColumnNumber`                          `!OperationalError`
+``HV009`` `!FdwInvalidUseOfNullPointer`                      `!OperationalError`
+``HV00A`` `!FdwInvalidStringFormat`                          `!OperationalError`
+``HV00B`` `!FdwInvalidHandle`                                `!OperationalError`
+``HV00C`` `!FdwInvalidOptionIndex`                           `!OperationalError`
+``HV00D`` `!FdwInvalidOptionName`                            `!OperationalError`
+``HV00J`` `!FdwOptionNameNotFound`                           `!OperationalError`
+``HV00K`` `!FdwReplyHandle`                                  `!OperationalError`
+``HV00L`` `!FdwUnableToCreateExecution`                      `!OperationalError`
+``HV00M`` `!FdwUnableToCreateReply`                          `!OperationalError`
+``HV00N`` `!FdwUnableToEstablishConnection`                  `!OperationalError`
+``HV00P`` `!FdwNoSchemas`                                    `!OperationalError`
+``HV00Q`` `!FdwSchemaNotFound`                               `!OperationalError`
+``HV00R`` `!FdwTableNotFound`                                `!OperationalError`
+``HV010`` `!FdwFunctionSequenceError`                        `!OperationalError`
+``HV014`` `!FdwTooManyHandles`                               `!OperationalError`
+``HV021`` `!FdwInconsistentDescriptorInformation`            `!OperationalError`
+``HV024`` `!FdwInvalidAttributeValue`                        `!OperationalError`
+``HV090`` `!FdwInvalidStringLengthOrBufferLength`            `!OperationalError`
+``HV091`` `!FdwInvalidDescriptorFieldIdentifier`             `!OperationalError`
+**Class P0**: Class P0 - PL/pgSQL Error
+---------------------------------------------------------------------------------
+``P0000`` `!PlpgsqlError`                                    `!ProgrammingError`
+``P0001`` `!RaiseException`                                  `!ProgrammingError`
+``P0002`` `!NoDataFound`                                     `!ProgrammingError`
+``P0003`` `!TooManyRows`                                     `!ProgrammingError`
+``P0004`` `!AssertFailure`                                   `!ProgrammingError`
+**Class XX**: Class XX - Internal Error
+---------------------------------------------------------------------------------
+``XX000`` `!InternalError_`                                  `!InternalError`
+``XX001`` `!DataCorrupted`                                   `!InternalError`
+``XX002`` `!IndexCorrupted`                                  `!InternalError`
+========= ================================================== ====================
+
+.. autogenerated: end
index b2a7245b15158e8007e36e79aabdc8c78c51a04b..8e5a347aebaa062af39e49d6332068921da6c8a0 100644 (file)
@@ -25,6 +25,7 @@ the COPY support.
     connection
     cursor
     sql
+    errors
     from_pg2
 
 
index 996c0d33a65e6385aef16dc3b00143d7dc6076d5..ea51fbbbc65b1c5bf6e8c3290f36a86147122ac9 100644 (file)
@@ -28,7 +28,7 @@ class Warning(Exception):
     """
     Exception raised for important warnings.
 
-    For example data truncations while inserting, etc.
+    Defined for DBAPI compatibility, but never raised by ``psycopg3``.
     """
 
 
@@ -38,6 +38,11 @@ ErrorInfo = Union[None, PGresult, Dict[int, Optional[bytes]]]
 class Error(Exception):
     """
     Base exception for all the errors psycopg3 will raise.
+
+    Exception that is the base class of all other error exceptions. You can
+    use this to catch all errors with one single `!except` statement.
+
+    This exception is guaranteed to be picklable.
     """
 
     def __init__(
@@ -52,6 +57,9 @@ class Error(Exception):
 
     @property
     def diag(self) -> "Diagnostic":
+        """
+        A `Diagnostic` object to inspect details of the errors from the database.
+        """
         return Diagnostic(self._info, encoding=self._encoding)
 
     def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
@@ -82,13 +90,13 @@ class InterfaceError(Error):
 
 class DatabaseError(Error):
     """
-    An error related to the database.
+    Exception raised for errors that are related to the database.
     """
 
 
 class DataError(DatabaseError):
     """
-    An error caused by  problems with the processed data.
+    An error caused by problems with the processed data.
 
     Examples may be division by zero, numeric value out of range, etc.
     """
@@ -138,6 +146,8 @@ class NotSupportedError(DatabaseError):
 
 
 class Diagnostic:
+    """Details from a database error report."""
+
     def __init__(self, info: ErrorInfo, encoding: str = "utf-8"):
         self._info = info
         self._encoding = encoding
@@ -235,6 +245,10 @@ class Diagnostic:
 
 
 def lookup(sqlstate: str) -> Type[Error]:
+    """Lookup an error code and return its exception class.
+
+    Raise `!KeyError` if the code is not found.
+    """
     return _sqlcodes[sqlstate]
 
 
index 4ec60f18044d14dc0e8076db82abc2d372c96162..523f14297f274ccc26f7f44d02f025b9646eabb4 100755 (executable)
@@ -13,9 +13,9 @@ import sys
 import logging
 import subprocess as sp
 from urllib.request import urlopen
-from collections import defaultdict
+from collections import defaultdict, namedtuple
 
-from psycopg3.errors import get_base_exception
+from psycopg3.errors import get_base_exception  # type: ignore
 
 logger = logging.getLogger()
 logging.basicConfig(
@@ -24,28 +24,17 @@ logging.basicConfig(
 
 
 def main():
+    classes, errors = fetch_errors(["9.6", "10", "11", "12", "13"])
 
     fn = os.path.dirname(__file__) + "/../psycopg3/psycopg3/errors.py"
-
-    with open(fn, "r") as f:
-        lines = f.read().splitlines()
-
-    istart, iend = [
-        i
-        for i, line in enumerate(lines)
-        if re.match(r"\s*#\s*autogenerated:\s+(start|end)", line)
-    ]
-
-    classes, errors = fetch_errors(["9.5", "9.6", "10", "11", "12", "13"])
-    lines[istart + 1 : iend] = generate_module_data(classes, errors)
-
-    with open(fn, "w") as f:
-        for line in lines:
-            f.write(line + "\n")
+    update_file(fn, generate_module_data(classes, errors))
 
     logger.info("running black on the resulting module")
     sp.check_call(["black", fn])
 
+    fn = os.path.dirname(__file__) + "/../docs/errors.rst"
+    update_file(fn, generate_docs_data(classes, errors))
+
 
 def parse_errors_txt(url):
     classes = {}
@@ -70,12 +59,12 @@ def parse_errors_txt(url):
             r"(.....)\s+(?:E|W|S)\s+ERRCODE_(\S+)(?:\s+(\S+))?$", line
         )
         if m:
-            errcode, macro, spec = m.groups()
-            # skip errcodes without specs as they are not publically visible
+            sqlstate, macro, spec = m.groups()
+            # skip sqlstates without specs as they are not publically visible
             if not spec:
                 continue
             errlabel = spec.upper()
-            errors[class_][errcode] = errlabel
+            errors[class_][sqlstate] = errlabel
             continue
 
         # We don't expect anything else
@@ -90,6 +79,9 @@ errors_txt_url = (
 )
 
 
+Error = namedtuple("Error", "sqlstate errlabel clsname basename")
+
+
 def fetch_errors(versions):
     classes = {}
     errors = defaultdict(dict)
@@ -107,15 +99,14 @@ def fetch_errors(versions):
         for c, cerrs in e1.items():
             errors[c].update(cerrs)
 
-    return classes, errors
+    # clean up data
 
+    # success and warning - never raised
+    del classes["00"]
+    del classes["01"]
+    del errors["00"]
+    del errors["01"]
 
-def generate_module_data(classes, errors):
-    tmpl = """
-@sqlcode(%(errcode)r)
-class %(cls)s(%(base)s):
-    pass
-"""
     specific = {
         "38002": "ModifyingSqlDataNotPermittedExt",
         "38003": "ProhibitedSqlStatementAttemptedExt",
@@ -131,29 +122,99 @@ class %(cls)s(%(base)s):
         """.split()
     )
 
-    for clscode, clslabel in sorted(classes.items()):
-        if clscode in ("00", "01"):
-            # success and warning - never raised
-            continue
-
-        yield f"\n# {clslabel}"
-
-        for errcode, errlabel in sorted(errors[clscode].items()):
-            if errcode in specific:
-                clsname = specific[errcode]
+    for c, cerrs in errors.items():
+        for sqstate, errlabel in list(cerrs.items()):
+            if sqstate in specific:
+                clsname = specific[sqstate]
             else:
                 clsname = errlabel.title().replace("_", "")
             if clsname in seen:
                 raise Exception("class already existing: %s" % clsname)
             seen.add(clsname)
 
-            base = get_base_exception(errcode)
+            basename = get_base_exception(sqstate).__name__
+            cerrs[sqstate] = Error(sqstate, errlabel, clsname, basename)
+
+    return classes, errors
+
+
+def generate_module_data(classes, errors):
+    tmpl = """
+@sqlcode(%(sqlstate)r)
+class %(clsname)s(%(basename)s):
+    pass
+"""
+    for clscode, clslabel in sorted(classes.items()):
+        yield f"\n# {clslabel}"
+
+        for _, error in sorted(errors[clscode].items()):
+            yield tmpl % error._asdict()
+
+
+def generate_docs_data(classes, errors):
+    Line = namedtuple("Line", "colstate colexc colbase, sqlstate")
+    lines = [Line("SQLSTATE", "Exception", "Base exception", None)]
+
+    for clscode in sorted(classes):
+        for _, error in sorted(errors[clscode].items()):
+            lines.append(
+                Line(
+                    f"``{error.sqlstate}``",
+                    f"`!{error.clsname}`",
+                    f"`!{error.basename}`",
+                    error.sqlstate,
+                )
+            )
+
+    widths = [max(len(line[c]) for line in lines) for c in range(3)]
+    h = Line(*(["=" * w for w in widths] + [None]))
+    lines.insert(0, h)
+    lines.insert(2, h)
+    lines.append(h)
+
+    h1 = "-" * (sum(widths) + len(widths) - 1)
+    sqlclass = None
+
+    yield ""
+    for line in lines:
+        cls = line.sqlstate[:2] if line.sqlstate else None
+        if cls and cls != sqlclass:
+            yield "**Class %s**: %s" % (cls, classes[cls])
+            yield h1
+            sqlclass = cls
+
+        yield (
+            "%-*s %-*s %-*s"
+            % (
+                widths[0],
+                line.colstate,
+                widths[1],
+                line.colexc,
+                widths[2],
+                line.colbase,
+            )
+        ).rstrip()
+
+    yield ""
+
+
+def update_file(fn, new_lines):
+    logger.info("updating %s", fn)
 
-            yield tmpl % {
-                "cls": clsname,
-                "errcode": errcode,
-                "base": base.__name__,
-            }
+    with open(fn, "r") as f:
+        lines = f.read().splitlines()
+
+    istart, iend = [
+        i
+        for i, line in enumerate(lines)
+        if re.match(r"\s*(#|\.\.)\s*autogenerated:\s+(start|end)", line)
+    ]
+
+    lines[istart + 1 : iend] = new_lines
+
+    with open(fn, "w") as f:
+        for line in lines:
+            f.write(line + "\n")
 
 
 if __name__ == "__main__":