From 30c1befa64fa50159b4fc3b020ae18ae797dfd58 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Thu, 7 Feb 2019 11:36:44 +0000 Subject: [PATCH 1/4] SQLSTATE error classes implemented in C The module is only used to export them to Python. --- lib/errors.py | 1329 +------------------------------------ psycopg/error.h | 2 + psycopg/error_type.c | 98 +++ psycopg/pqpath.c | 124 ---- psycopg/psycopg.h | 3 + psycopg/psycopgmodule.c | 97 ++- psycopg/sqlstate_errors.h | 318 +++++++++ scripts/make_errors.py | 103 +-- setup.py | 2 +- tests/test_errors.py | 25 +- 10 files changed, 571 insertions(+), 1530 deletions(-) create mode 100644 psycopg/sqlstate_errors.h diff --git a/lib/errors.py b/lib/errors.py index 99ab0b04..79d3ff42 100644 --- a/lib/errors.py +++ b/lib/errors.py @@ -1,13 +1,31 @@ """Error classes for PostgreSQL error codes """ -# Imported for completeness, but unused in this module. -from psycopg2._psycopg import Error, Warning, InterfaceError # noqa +# psycopg/errors.py - SQLSTATE and DB-API exceptions +# +# Copyright (C) 2018-2019 Daniele Varrazzo +# +# psycopg2 is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# In addition, as a special exception, the copyright holders give +# permission to link this program with the OpenSSL library (or with +# modified versions of OpenSSL that use the same license as OpenSSL), +# and distribute linked combinations including the two. +# +# You must obey the GNU Lesser General Public License in all respects for +# all of the code used other than OpenSSL. +# +# psycopg2 is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. -from psycopg2._psycopg import ( - DataError, DatabaseError, ProgrammingError, IntegrityError, - InternalError, NotSupportedError, OperationalError, - QueryCanceledError, TransactionRollbackError) +# +# NOTE: the exceptions are injected into this module by the C extention. +# def lookup(code): @@ -15,1300 +33,5 @@ def lookup(code): Raise `!KeyError` if the code is not found. """ - return _by_sqlstate[code] - - -_by_sqlstate = {} - - -def for_sqlstate(state): - def for_sqlstate_(cls): - _by_sqlstate[state] = cls - return cls - return for_sqlstate_ - - -# autogenerated data: do not edit below this point. - - -# Class 02 - No Data (this is also a warning class per the SQL standard) - - -@for_sqlstate('02000') -class NoData(DatabaseError): - pass - - -@for_sqlstate('02001') -class NoAdditionalDynamicResultSetsReturned(DatabaseError): - pass - - -# Class 03 - SQL Statement Not Yet Complete - - -@for_sqlstate('03000') -class SqlStatementNotYetComplete(DatabaseError): - pass - - -# Class 08 - Connection Exception - - -@for_sqlstate('08000') -class ConnectionException(DatabaseError): - pass - - -@for_sqlstate('08001') -class SqlclientUnableToEstablishSqlconnection(DatabaseError): - pass - - -@for_sqlstate('08003') -class ConnectionDoesNotExist(DatabaseError): - pass - - -@for_sqlstate('08004') -class SqlserverRejectedEstablishmentOfSqlconnection(DatabaseError): - pass - - -@for_sqlstate('08006') -class ConnectionFailure(DatabaseError): - pass - - -@for_sqlstate('08007') -class TransactionResolutionUnknown(DatabaseError): - pass - - -@for_sqlstate('08P01') -class ProtocolViolation(DatabaseError): - pass - - -# Class 09 - Triggered Action Exception - - -@for_sqlstate('09000') -class TriggeredActionException(DatabaseError): - pass - - -# Class 0A - Feature Not Supported - - -@for_sqlstate('0A000') -class FeatureNotSupported(NotSupportedError): - pass - - -# Class 0B - Invalid Transaction Initiation - - -@for_sqlstate('0B000') -class InvalidTransactionInitiation(DatabaseError): - pass - - -# Class 0F - Locator Exception - - -@for_sqlstate('0F000') -class LocatorException(DatabaseError): - pass - - -@for_sqlstate('0F001') -class InvalidLocatorSpecification(DatabaseError): - pass - - -# Class 0L - Invalid Grantor - - -@for_sqlstate('0L000') -class InvalidGrantor(DatabaseError): - pass - - -@for_sqlstate('0LP01') -class InvalidGrantOperation(DatabaseError): - pass - - -# Class 0P - Invalid Role Specification - - -@for_sqlstate('0P000') -class InvalidRoleSpecification(DatabaseError): - pass - - -# Class 0Z - Diagnostics Exception - - -@for_sqlstate('0Z000') -class DiagnosticsException(DatabaseError): - pass - - -@for_sqlstate('0Z002') -class StackedDiagnosticsAccessedWithoutActiveHandler(DatabaseError): - pass - - -# Class 20 - Case Not Found - - -@for_sqlstate('20000') -class CaseNotFound(ProgrammingError): - pass - - -# Class 21 - Cardinality Violation - - -@for_sqlstate('21000') -class CardinalityViolation(ProgrammingError): - pass - - -# Class 22 - Data Exception - - -@for_sqlstate('22000') -class DataException(DataError): - pass - - -@for_sqlstate('22001') -class StringDataRightTruncation(DataError): - pass - - -@for_sqlstate('22002') -class NullValueNoIndicatorParameter(DataError): - pass - - -@for_sqlstate('22003') -class NumericValueOutOfRange(DataError): - pass - - -@for_sqlstate('22004') -class NullValueNotAllowed(DataError): - pass - - -@for_sqlstate('22005') -class ErrorInAssignment(DataError): - pass - - -@for_sqlstate('22007') -class InvalidDatetimeFormat(DataError): - pass - - -@for_sqlstate('22008') -class DatetimeFieldOverflow(DataError): - pass - - -@for_sqlstate('22009') -class InvalidTimeZoneDisplacementValue(DataError): - pass - - -@for_sqlstate('2200B') -class EscapeCharacterConflict(DataError): - pass - - -@for_sqlstate('2200C') -class InvalidUseOfEscapeCharacter(DataError): - pass - - -@for_sqlstate('2200D') -class InvalidEscapeOctet(DataError): - pass - - -@for_sqlstate('2200F') -class ZeroLengthCharacterString(DataError): - pass - - -@for_sqlstate('2200G') -class MostSpecificTypeMismatch(DataError): - pass - - -@for_sqlstate('2200H') -class SequenceGeneratorLimitExceeded(DataError): - pass - - -@for_sqlstate('2200L') -class NotAnXmlDocument(DataError): - pass - - -@for_sqlstate('2200M') -class InvalidXmlDocument(DataError): - pass - - -@for_sqlstate('2200N') -class InvalidXmlContent(DataError): - pass - - -@for_sqlstate('2200S') -class InvalidXmlComment(DataError): - pass - - -@for_sqlstate('2200T') -class InvalidXmlProcessingInstruction(DataError): - pass - - -@for_sqlstate('22010') -class InvalidIndicatorParameterValue(DataError): - pass - - -@for_sqlstate('22011') -class SubstringError(DataError): - pass - - -@for_sqlstate('22012') -class DivisionByZero(DataError): - pass - - -@for_sqlstate('22013') -class InvalidPrecedingOrFollowingSize(DataError): - pass - - -@for_sqlstate('22014') -class InvalidArgumentForNtileFunction(DataError): - pass - - -@for_sqlstate('22015') -class IntervalFieldOverflow(DataError): - pass - - -@for_sqlstate('22016') -class InvalidArgumentForNthValueFunction(DataError): - pass - - -@for_sqlstate('22018') -class InvalidCharacterValueForCast(DataError): - pass - - -@for_sqlstate('22019') -class InvalidEscapeCharacter(DataError): - pass - - -@for_sqlstate('2201B') -class InvalidRegularExpression(DataError): - pass - - -@for_sqlstate('2201E') -class InvalidArgumentForLogarithm(DataError): - pass - - -@for_sqlstate('2201F') -class InvalidArgumentForPowerFunction(DataError): - pass - - -@for_sqlstate('2201G') -class InvalidArgumentForWidthBucketFunction(DataError): - pass - - -@for_sqlstate('2201W') -class InvalidRowCountInLimitClause(DataError): - pass - - -@for_sqlstate('2201X') -class InvalidRowCountInResultOffsetClause(DataError): - pass - - -@for_sqlstate('22021') -class CharacterNotInRepertoire(DataError): - pass - - -@for_sqlstate('22022') -class IndicatorOverflow(DataError): - pass - - -@for_sqlstate('22023') -class InvalidParameterValue(DataError): - pass - - -@for_sqlstate('22024') -class UnterminatedCString(DataError): - pass - - -@for_sqlstate('22025') -class InvalidEscapeSequence(DataError): - pass - - -@for_sqlstate('22026') -class StringDataLengthMismatch(DataError): - pass - - -@for_sqlstate('22027') -class TrimError(DataError): - pass - - -@for_sqlstate('2202E') -class ArraySubscriptError(DataError): - pass - - -@for_sqlstate('2202G') -class InvalidTablesampleRepeat(DataError): - pass - - -@for_sqlstate('2202H') -class InvalidTablesampleArgument(DataError): - pass - - -@for_sqlstate('22P01') -class FloatingPointException(DataError): - pass - - -@for_sqlstate('22P02') -class InvalidTextRepresentation(DataError): - pass - - -@for_sqlstate('22P03') -class InvalidBinaryRepresentation(DataError): - pass - - -@for_sqlstate('22P04') -class BadCopyFileFormat(DataError): - pass - - -@for_sqlstate('22P05') -class UntranslatableCharacter(DataError): - pass - - -@for_sqlstate('22P06') -class NonstandardUseOfEscapeCharacter(DataError): - pass - - -# Class 23 - Integrity Constraint Violation - - -@for_sqlstate('23000') -class IntegrityConstraintViolation(IntegrityError): - pass - - -@for_sqlstate('23001') -class RestrictViolation(IntegrityError): - pass - - -@for_sqlstate('23502') -class NotNullViolation(IntegrityError): - pass - - -@for_sqlstate('23503') -class ForeignKeyViolation(IntegrityError): - pass - - -@for_sqlstate('23505') -class UniqueViolation(IntegrityError): - pass - - -@for_sqlstate('23514') -class CheckViolation(IntegrityError): - pass - - -@for_sqlstate('23P01') -class ExclusionViolation(IntegrityError): - pass - - -# Class 24 - Invalid Cursor State - - -@for_sqlstate('24000') -class InvalidCursorState(InternalError): - pass - - -# Class 25 - Invalid Transaction State - - -@for_sqlstate('25000') -class InvalidTransactionState(InternalError): - pass - - -@for_sqlstate('25001') -class ActiveSqlTransaction(InternalError): - pass - - -@for_sqlstate('25002') -class BranchTransactionAlreadyActive(InternalError): - pass - - -@for_sqlstate('25003') -class InappropriateAccessModeForBranchTransaction(InternalError): - pass - - -@for_sqlstate('25004') -class InappropriateIsolationLevelForBranchTransaction(InternalError): - pass - - -@for_sqlstate('25005') -class NoActiveSqlTransactionForBranchTransaction(InternalError): - pass - - -@for_sqlstate('25006') -class ReadOnlySqlTransaction(InternalError): - pass - - -@for_sqlstate('25007') -class SchemaAndDataStatementMixingNotSupported(InternalError): - pass - - -@for_sqlstate('25008') -class HeldCursorRequiresSameIsolationLevel(InternalError): - pass - - -@for_sqlstate('25P01') -class NoActiveSqlTransaction(InternalError): - pass - - -@for_sqlstate('25P02') -class InFailedSqlTransaction(InternalError): - pass - - -@for_sqlstate('25P03') -class IdleInTransactionSessionTimeout(InternalError): - pass - - -# Class 26 - Invalid SQL Statement Name - - -@for_sqlstate('26000') -class InvalidSqlStatementName(OperationalError): - pass - - -# Class 27 - Triggered Data Change Violation - - -@for_sqlstate('27000') -class TriggeredDataChangeViolation(OperationalError): - pass - - -# Class 28 - Invalid Authorization Specification - - -@for_sqlstate('28000') -class InvalidAuthorizationSpecification(OperationalError): - pass - - -@for_sqlstate('28P01') -class InvalidPassword(OperationalError): - pass - - -# Class 2B - Dependent Privilege Descriptors Still Exist - - -@for_sqlstate('2B000') -class DependentPrivilegeDescriptorsStillExist(InternalError): - pass - - -@for_sqlstate('2BP01') -class DependentObjectsStillExist(InternalError): - pass - - -# Class 2D - Invalid Transaction Termination - - -@for_sqlstate('2D000') -class InvalidTransactionTermination(InternalError): - pass - - -# Class 2F - SQL Routine Exception - - -@for_sqlstate('2F000') -class SqlRoutineException(InternalError): - pass - - -@for_sqlstate('2F002') -class ModifyingSqlDataNotPermitted(InternalError): - pass - - -@for_sqlstate('2F003') -class ProhibitedSqlStatementAttempted(InternalError): - pass - - -@for_sqlstate('2F004') -class ReadingSqlDataNotPermitted(InternalError): - pass - - -@for_sqlstate('2F005') -class FunctionExecutedNoReturnStatement(InternalError): - pass - - -# Class 34 - Invalid Cursor Name - - -@for_sqlstate('34000') -class InvalidCursorName(OperationalError): - pass - - -# Class 38 - External Routine Exception - - -@for_sqlstate('38000') -class ExternalRoutineException(InternalError): - pass - - -@for_sqlstate('38001') -class ContainingSqlNotPermitted(InternalError): - pass - - -@for_sqlstate('38002') -class ModifyingSqlDataNotPermittedExt(InternalError): - pass - - -@for_sqlstate('38003') -class ProhibitedSqlStatementAttemptedExt(InternalError): - pass - - -@for_sqlstate('38004') -class ReadingSqlDataNotPermittedExt(InternalError): - pass - - -# Class 39 - External Routine Invocation Exception - - -@for_sqlstate('39000') -class ExternalRoutineInvocationException(InternalError): - pass - - -@for_sqlstate('39001') -class InvalidSqlstateReturned(InternalError): - pass - - -@for_sqlstate('39004') -class NullValueNotAllowedExt(InternalError): - pass - - -@for_sqlstate('39P01') -class TriggerProtocolViolated(InternalError): - pass - - -@for_sqlstate('39P02') -class SrfProtocolViolated(InternalError): - pass - - -@for_sqlstate('39P03') -class EventTriggerProtocolViolated(InternalError): - pass - - -# Class 3B - Savepoint Exception - - -@for_sqlstate('3B000') -class SavepointException(InternalError): - pass - - -@for_sqlstate('3B001') -class InvalidSavepointSpecification(InternalError): - pass - - -# Class 3D - Invalid Catalog Name - - -@for_sqlstate('3D000') -class InvalidCatalogName(ProgrammingError): - pass - - -# Class 3F - Invalid Schema Name - - -@for_sqlstate('3F000') -class InvalidSchemaName(ProgrammingError): - pass - - -# Class 40 - Transaction Rollback - - -@for_sqlstate('40000') -class TransactionRollback(TransactionRollbackError): - pass - - -@for_sqlstate('40001') -class SerializationFailure(TransactionRollbackError): - pass - - -@for_sqlstate('40002') -class TransactionIntegrityConstraintViolation(TransactionRollbackError): - pass - - -@for_sqlstate('40003') -class StatementCompletionUnknown(TransactionRollbackError): - pass - - -@for_sqlstate('40P01') -class DeadlockDetected(TransactionRollbackError): - pass - - -# Class 42 - Syntax Error or Access Rule Violation - - -@for_sqlstate('42000') -class SyntaxErrorOrAccessRuleViolation(ProgrammingError): - pass - - -@for_sqlstate('42501') -class InsufficientPrivilege(ProgrammingError): - pass - - -@for_sqlstate('42601') -class SyntaxError(ProgrammingError): - pass - - -@for_sqlstate('42602') -class InvalidName(ProgrammingError): - pass - - -@for_sqlstate('42611') -class InvalidColumnDefinition(ProgrammingError): - pass - - -@for_sqlstate('42622') -class NameTooLong(ProgrammingError): - pass - - -@for_sqlstate('42701') -class DuplicateColumn(ProgrammingError): - pass - - -@for_sqlstate('42702') -class AmbiguousColumn(ProgrammingError): - pass - - -@for_sqlstate('42703') -class UndefinedColumn(ProgrammingError): - pass - - -@for_sqlstate('42704') -class UndefinedObject(ProgrammingError): - pass - - -@for_sqlstate('42710') -class DuplicateObject(ProgrammingError): - pass - - -@for_sqlstate('42712') -class DuplicateAlias(ProgrammingError): - pass - - -@for_sqlstate('42723') -class DuplicateFunction(ProgrammingError): - pass - - -@for_sqlstate('42725') -class AmbiguousFunction(ProgrammingError): - pass - - -@for_sqlstate('42803') -class GroupingError(ProgrammingError): - pass - - -@for_sqlstate('42804') -class DatatypeMismatch(ProgrammingError): - pass - - -@for_sqlstate('42809') -class WrongObjectType(ProgrammingError): - pass - - -@for_sqlstate('42830') -class InvalidForeignKey(ProgrammingError): - pass - - -@for_sqlstate('42846') -class CannotCoerce(ProgrammingError): - pass - - -@for_sqlstate('42883') -class UndefinedFunction(ProgrammingError): - pass - - -@for_sqlstate('428C9') -class GeneratedAlways(ProgrammingError): - pass - - -@for_sqlstate('42939') -class ReservedName(ProgrammingError): - pass - - -@for_sqlstate('42P01') -class UndefinedTable(ProgrammingError): - pass - - -@for_sqlstate('42P02') -class UndefinedParameter(ProgrammingError): - pass - - -@for_sqlstate('42P03') -class DuplicateCursor(ProgrammingError): - pass - - -@for_sqlstate('42P04') -class DuplicateDatabase(ProgrammingError): - pass - - -@for_sqlstate('42P05') -class DuplicatePreparedStatement(ProgrammingError): - pass - - -@for_sqlstate('42P06') -class DuplicateSchema(ProgrammingError): - pass - - -@for_sqlstate('42P07') -class DuplicateTable(ProgrammingError): - pass - - -@for_sqlstate('42P08') -class AmbiguousParameter(ProgrammingError): - pass - - -@for_sqlstate('42P09') -class AmbiguousAlias(ProgrammingError): - pass - - -@for_sqlstate('42P10') -class InvalidColumnReference(ProgrammingError): - pass - - -@for_sqlstate('42P11') -class InvalidCursorDefinition(ProgrammingError): - pass - - -@for_sqlstate('42P12') -class InvalidDatabaseDefinition(ProgrammingError): - pass - - -@for_sqlstate('42P13') -class InvalidFunctionDefinition(ProgrammingError): - pass - - -@for_sqlstate('42P14') -class InvalidPreparedStatementDefinition(ProgrammingError): - pass - - -@for_sqlstate('42P15') -class InvalidSchemaDefinition(ProgrammingError): - pass - - -@for_sqlstate('42P16') -class InvalidTableDefinition(ProgrammingError): - pass - - -@for_sqlstate('42P17') -class InvalidObjectDefinition(ProgrammingError): - pass - - -@for_sqlstate('42P18') -class IndeterminateDatatype(ProgrammingError): - pass - - -@for_sqlstate('42P19') -class InvalidRecursion(ProgrammingError): - pass - - -@for_sqlstate('42P20') -class WindowingError(ProgrammingError): - pass - - -@for_sqlstate('42P21') -class CollationMismatch(ProgrammingError): - pass - - -@for_sqlstate('42P22') -class IndeterminateCollation(ProgrammingError): - pass - - -# Class 44 - WITH CHECK OPTION Violation - - -@for_sqlstate('44000') -class WithCheckOptionViolation(ProgrammingError): - pass - - -# Class 53 - Insufficient Resources - - -@for_sqlstate('53000') -class InsufficientResources(OperationalError): - pass - - -@for_sqlstate('53100') -class DiskFull(OperationalError): - pass - - -@for_sqlstate('53200') -class OutOfMemory(OperationalError): - pass - - -@for_sqlstate('53300') -class TooManyConnections(OperationalError): - pass - - -@for_sqlstate('53400') -class ConfigurationLimitExceeded(OperationalError): - pass - - -# Class 54 - Program Limit Exceeded - - -@for_sqlstate('54000') -class ProgramLimitExceeded(OperationalError): - pass - - -@for_sqlstate('54001') -class StatementTooComplex(OperationalError): - pass - - -@for_sqlstate('54011') -class TooManyColumns(OperationalError): - pass - - -@for_sqlstate('54023') -class TooManyArguments(OperationalError): - pass - - -# Class 55 - Object Not In Prerequisite State - - -@for_sqlstate('55000') -class ObjectNotInPrerequisiteState(OperationalError): - pass - - -@for_sqlstate('55006') -class ObjectInUse(OperationalError): - pass - - -@for_sqlstate('55P02') -class CantChangeRuntimeParam(OperationalError): - pass - - -@for_sqlstate('55P03') -class LockNotAvailable(OperationalError): - pass - - -# Class 57 - Operator Intervention - - -@for_sqlstate('57000') -class OperatorIntervention(OperationalError): - pass - - -@for_sqlstate('57014') -class QueryCanceled(QueryCanceledError): - pass - - -@for_sqlstate('57P01') -class AdminShutdown(OperationalError): - pass - - -@for_sqlstate('57P02') -class CrashShutdown(OperationalError): - pass - - -@for_sqlstate('57P03') -class CannotConnectNow(OperationalError): - pass - - -@for_sqlstate('57P04') -class DatabaseDropped(OperationalError): - pass - - -# Class 58 - System Error (errors external to PostgreSQL itself) - - -@for_sqlstate('58000') -class SystemError(OperationalError): - pass - - -@for_sqlstate('58030') -class IoError(OperationalError): - pass - - -@for_sqlstate('58P01') -class UndefinedFile(OperationalError): - pass - - -@for_sqlstate('58P02') -class DuplicateFile(OperationalError): - pass - - -# Class 72 - Snapshot Failure - - -@for_sqlstate('72000') -class SnapshotTooOld(DatabaseError): - pass - - -# Class F0 - Configuration File Error - - -@for_sqlstate('F0000') -class ConfigFileError(InternalError): - pass - - -@for_sqlstate('F0001') -class LockFileExists(InternalError): - pass - - -# Class HV - Foreign Data Wrapper Error (SQL/MED) - - -@for_sqlstate('HV000') -class FdwError(OperationalError): - pass - - -@for_sqlstate('HV001') -class FdwOutOfMemory(OperationalError): - pass - - -@for_sqlstate('HV002') -class FdwDynamicParameterValueNeeded(OperationalError): - pass - - -@for_sqlstate('HV004') -class FdwInvalidDataType(OperationalError): - pass - - -@for_sqlstate('HV005') -class FdwColumnNameNotFound(OperationalError): - pass - - -@for_sqlstate('HV006') -class FdwInvalidDataTypeDescriptors(OperationalError): - pass - - -@for_sqlstate('HV007') -class FdwInvalidColumnName(OperationalError): - pass - - -@for_sqlstate('HV008') -class FdwInvalidColumnNumber(OperationalError): - pass - - -@for_sqlstate('HV009') -class FdwInvalidUseOfNullPointer(OperationalError): - pass - - -@for_sqlstate('HV00A') -class FdwInvalidStringFormat(OperationalError): - pass - - -@for_sqlstate('HV00B') -class FdwInvalidHandle(OperationalError): - pass - - -@for_sqlstate('HV00C') -class FdwInvalidOptionIndex(OperationalError): - pass - - -@for_sqlstate('HV00D') -class FdwInvalidOptionName(OperationalError): - pass - - -@for_sqlstate('HV00J') -class FdwOptionNameNotFound(OperationalError): - pass - - -@for_sqlstate('HV00K') -class FdwReplyHandle(OperationalError): - pass - - -@for_sqlstate('HV00L') -class FdwUnableToCreateExecution(OperationalError): - pass - - -@for_sqlstate('HV00M') -class FdwUnableToCreateReply(OperationalError): - pass - - -@for_sqlstate('HV00N') -class FdwUnableToEstablishConnection(OperationalError): - pass - - -@for_sqlstate('HV00P') -class FdwNoSchemas(OperationalError): - pass - - -@for_sqlstate('HV00Q') -class FdwSchemaNotFound(OperationalError): - pass - - -@for_sqlstate('HV00R') -class FdwTableNotFound(OperationalError): - pass - - -@for_sqlstate('HV010') -class FdwFunctionSequenceError(OperationalError): - pass - - -@for_sqlstate('HV014') -class FdwTooManyHandles(OperationalError): - pass - - -@for_sqlstate('HV021') -class FdwInconsistentDescriptorInformation(OperationalError): - pass - - -@for_sqlstate('HV024') -class FdwInvalidAttributeValue(OperationalError): - pass - - -@for_sqlstate('HV090') -class FdwInvalidStringLengthOrBufferLength(OperationalError): - pass - - -@for_sqlstate('HV091') -class FdwInvalidDescriptorFieldIdentifier(OperationalError): - pass - - -# Class P0 - PL/pgSQL Error - - -@for_sqlstate('P0000') -class PlpgsqlError(InternalError): - pass - - -@for_sqlstate('P0001') -class RaiseException(InternalError): - pass - - -@for_sqlstate('P0002') -class NoDataFound(InternalError): - pass - - -@for_sqlstate('P0003') -class TooManyRows(InternalError): - pass - - -@for_sqlstate('P0004') -class AssertFailure(InternalError): - pass - - -# Class XX - Internal Error - - -@for_sqlstate('XX000') -class InternalError_(InternalError): - pass - - -@for_sqlstate('XX001') -class DataCorrupted(InternalError): - pass - - -@for_sqlstate('XX002') -class IndexCorrupted(InternalError): - pass + from psycopg2._psycopg import sqlstate_errors # avoid circular import + return sqlstate_errors[code] diff --git a/psycopg/error.h b/psycopg/error.h index 275a7ce7..a32042b0 100644 --- a/psycopg/error.h +++ b/psycopg/error.h @@ -39,5 +39,7 @@ typedef struct { } errorObject; HIDDEN PyObject *error_text_from_chars(errorObject *self, const char *str); +HIDDEN BORROWED PyObject *exception_from_sqlstate(const char *sqlstate); +HIDDEN BORROWED PyObject *base_exception_from_sqlstate(const char *sqlstate); #endif /* PSYCOPG_ERROR_H */ diff --git a/psycopg/error_type.c b/psycopg/error_type.c index 6dfdb527..ac40a0c5 100644 --- a/psycopg/error_type.c +++ b/psycopg/error_type.c @@ -38,6 +38,104 @@ error_text_from_chars(errorObject *self, const char *str) } +/* Return the Python exception corresponding to an SQLSTATE error + * code. A list of error codes can be found at: + * https://www.postgresql.org/docs/current/static/errcodes-appendix.html + */ +BORROWED PyObject * +exception_from_sqlstate(const char *sqlstate) +{ + PyObject *exc; + + /* First look up an exception of the proper class */ + exc = PyDict_GetItemString(sqlstate_errors, sqlstate); + if (exc) { + return exc; + } + else { + PyErr_Clear(); + return base_exception_from_sqlstate(sqlstate); + } +} + +BORROWED PyObject * +base_exception_from_sqlstate(const char *sqlstate) +{ + switch (sqlstate[0]) { + case '0': + switch (sqlstate[1]) { + case 'A': /* Class 0A - Feature Not Supported */ + return NotSupportedError; + } + break; + case '2': + switch (sqlstate[1]) { + case '0': /* Class 20 - Case Not Found */ + case '1': /* Class 21 - Cardinality Violation */ + return ProgrammingError; + case '2': /* Class 22 - Data Exception */ + return DataError; + case '3': /* Class 23 - Integrity Constraint Violation */ + return IntegrityError; + case '4': /* Class 24 - Invalid Cursor State */ + case '5': /* Class 25 - Invalid Transaction State */ + return InternalError; + case '6': /* Class 26 - Invalid SQL Statement Name */ + case '7': /* Class 27 - Triggered Data Change Violation */ + case '8': /* Class 28 - Invalid Authorization Specification */ + return OperationalError; + case 'B': /* Class 2B - Dependent Privilege Descriptors Still Exist */ + case 'D': /* Class 2D - Invalid Transaction Termination */ + case 'F': /* Class 2F - SQL Routine Exception */ + return InternalError; + } + break; + case '3': + switch (sqlstate[1]) { + case '4': /* Class 34 - Invalid Cursor Name */ + return OperationalError; + case '8': /* Class 38 - External Routine Exception */ + case '9': /* Class 39 - External Routine Invocation Exception */ + case 'B': /* Class 3B - Savepoint Exception */ + return InternalError; + case 'D': /* Class 3D - Invalid Catalog Name */ + case 'F': /* Class 3F - Invalid Schema Name */ + return ProgrammingError; + } + break; + case '4': + switch (sqlstate[1]) { + case '0': /* Class 40 - Transaction Rollback */ + return TransactionRollbackError; + case '2': /* Class 42 - Syntax Error or Access Rule Violation */ + case '4': /* Class 44 - WITH CHECK OPTION Violation */ + return ProgrammingError; + } + break; + case '5': + /* Class 53 - Insufficient Resources + Class 54 - Program Limit Exceeded + Class 55 - Object Not In Prerequisite State + Class 57 - Operator Intervention + Class 58 - System Error (errors external to PostgreSQL itself) */ + if (!strcmp(sqlstate, "57014")) + return QueryCanceledError; + else + return OperationalError; + case 'F': /* Class F0 - Configuration File Error */ + return InternalError; + case 'H': /* Class HV - Foreign Data Wrapper Error (SQL/MED) */ + return OperationalError; + case 'P': /* Class P0 - PL/pgSQL Error */ + return InternalError; + case 'X': /* Class XX - Internal Error */ + return InternalError; + } + /* return DatabaseError as a fallback */ + return DatabaseError; +} + + static const char pgerror_doc[] = "The error message returned by the backend, if available, else None"; diff --git a/psycopg/pqpath.c b/psycopg/pqpath.c index db5968a0..864c3956 100644 --- a/psycopg/pqpath.c +++ b/psycopg/pqpath.c @@ -77,130 +77,6 @@ strip_severity(const char *msg) return msg; } -/* Return a Python exception from a SQLSTATE from psycopg2.errors */ -BORROWED static PyObject * -exception_from_module(const char *sqlstate) -{ - PyObject *rv = NULL; - PyObject *m = NULL; - PyObject *map = NULL; - - if (!(m = PyImport_ImportModule("psycopg2.errors"))) { goto exit; } - if (!(map = PyObject_GetAttrString(m, "_by_sqlstate"))) { goto exit; } - if (!PyDict_Check(map)) { - Dprintf("'psycopg2.errors._by_sqlstate' is not a dict!"); - goto exit; - } - - /* get the sqlstate class (borrowed reference), or fail trying. */ - rv = PyDict_GetItemString(map, sqlstate); - -exit: - /* We exit with a borrowed object, or a NULL but no error - * If an error did happen in this function, we don't want to clobber the - * database error. So better reporting it, albeit with the wrong class. */ - PyErr_Clear(); - - Py_XDECREF(map); - Py_XDECREF(m); - return rv; -} - -/* Returns the Python exception corresponding to an SQLSTATE error - code. A list of error codes can be found at: - - https://www.postgresql.org/docs/current/static/errcodes-appendix.html */ -BORROWED static PyObject * -exception_from_sqlstate(const char *sqlstate) -{ - PyObject *exc; - - /* First look up an exception of the proper class from the Python module */ - exc = exception_from_module(sqlstate); - if (exc) { - return exc; - } - else { - PyErr_Clear(); - } - - /* - * IMPORTANT: if you change anything in this function you should change - * make_errors.py accordingly. - */ - switch (sqlstate[0]) { - case '0': - switch (sqlstate[1]) { - case 'A': /* Class 0A - Feature Not Supported */ - return NotSupportedError; - } - break; - case '2': - switch (sqlstate[1]) { - case '0': /* Class 20 - Case Not Found */ - case '1': /* Class 21 - Cardinality Violation */ - return ProgrammingError; - case '2': /* Class 22 - Data Exception */ - return DataError; - case '3': /* Class 23 - Integrity Constraint Violation */ - return IntegrityError; - case '4': /* Class 24 - Invalid Cursor State */ - case '5': /* Class 25 - Invalid Transaction State */ - return InternalError; - case '6': /* Class 26 - Invalid SQL Statement Name */ - case '7': /* Class 27 - Triggered Data Change Violation */ - case '8': /* Class 28 - Invalid Authorization Specification */ - return OperationalError; - case 'B': /* Class 2B - Dependent Privilege Descriptors Still Exist */ - case 'D': /* Class 2D - Invalid Transaction Termination */ - case 'F': /* Class 2F - SQL Routine Exception */ - return InternalError; - } - break; - case '3': - switch (sqlstate[1]) { - case '4': /* Class 34 - Invalid Cursor Name */ - return OperationalError; - case '8': /* Class 38 - External Routine Exception */ - case '9': /* Class 39 - External Routine Invocation Exception */ - case 'B': /* Class 3B - Savepoint Exception */ - return InternalError; - case 'D': /* Class 3D - Invalid Catalog Name */ - case 'F': /* Class 3F - Invalid Schema Name */ - return ProgrammingError; - } - break; - case '4': - switch (sqlstate[1]) { - case '0': /* Class 40 - Transaction Rollback */ - return TransactionRollbackError; - case '2': /* Class 42 - Syntax Error or Access Rule Violation */ - case '4': /* Class 44 - WITH CHECK OPTION Violation */ - return ProgrammingError; - } - break; - case '5': - /* Class 53 - Insufficient Resources - Class 54 - Program Limit Exceeded - Class 55 - Object Not In Prerequisite State - Class 57 - Operator Intervention - Class 58 - System Error (errors external to PostgreSQL itself) */ - if (!strcmp(sqlstate, "57014")) - return QueryCanceledError; - else - return OperationalError; - case 'F': /* Class F0 - Configuration File Error */ - return InternalError; - case 'H': /* Class HV - Foreign Data Wrapper Error (SQL/MED) */ - return OperationalError; - case 'P': /* Class P0 - PL/pgSQL Error */ - return InternalError; - case 'X': /* Class XX - Internal Error */ - return InternalError; - } - /* return DatabaseError as a fallback */ - return DatabaseError; -} /* pq_raise - raise a python exception of the right kind diff --git a/psycopg/psycopg.h b/psycopg/psycopg.h index 365a57c8..ca437376 100644 --- a/psycopg/psycopg.h +++ b/psycopg/psycopg.h @@ -53,6 +53,9 @@ extern HIDDEN PyObject *Error, *Warning, *InterfaceError, *DatabaseError, *IntegrityError, *DataError, *NotSupportedError; extern HIDDEN PyObject *QueryCanceledError, *TransactionRollbackError; +/* sqlstate -> exception map */ +extern HIDDEN PyObject *sqlstate_errors; + /* postgresql<->python encoding map */ extern HIDDEN PyObject *psycoEncodings; diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index 8f9aa537..122d275b 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -62,6 +62,8 @@ #include "psycopg/adapter_datetime.h" HIDDEN PyObject *psycoEncodings = NULL; +HIDDEN PyObject *sqlstate_errors = NULL; + #ifdef PSYCOPG_DEBUG HIDDEN int psycopg_debug_enabled = 0; #endif @@ -671,7 +673,7 @@ static struct { RAISES_NEG static int -errors_init(PyObject *module) +basic_errors_init(PyObject *module) { /* the names of the exceptions here reflect the organization of the psycopg2 module and not the fact the the original error objects @@ -680,6 +682,7 @@ errors_init(PyObject *module) int i; PyObject *dict = NULL; PyObject *str = NULL; + PyObject *errmodule = NULL; int rv = -1; Dprintf("psycopgmodule: initializing basic exceptions"); @@ -707,6 +710,10 @@ errors_init(PyObject *module) Py_CLEAR(dict); } + if (!(errmodule = PyImport_ImportModule("psycopg2.errors"))) { + goto exit; + } + for (i = 0; exctable[i].name; i++) { char *name; if (NULL == exctable[i].exc) { continue; } @@ -720,17 +727,102 @@ errors_init(PyObject *module) Py_DECREF(*exctable[i].exc); goto exit; } + Py_INCREF(*exctable[i].exc); + if (0 > PyModule_AddObject(errmodule, name, *exctable[i].exc)) { + Py_DECREF(*exctable[i].exc); + goto exit; + } } rv = 0; exit: + Py_XDECREF(errmodule); Py_XDECREF(str); Py_XDECREF(dict); return rv; } +/* mapping between sqlstate and exception name */ +static struct { + char *sqlstate; + char *name; +} sqlstate_table[] = { +#include "sqlstate_errors.h" + {NULL} /* Sentinel */ +}; + + +RAISES_NEG static int +sqlstate_errors_init(PyObject *module) +{ + int i; + char namebuf[120]; + char prefix[] = "psycopg2.errors."; + char *suffix; + size_t bufsize; + PyObject *exc = NULL; + PyObject *errmodule = NULL; + int rv = -1; + + Dprintf("psycopgmodule: initializing sqlstate exceptions"); + + if (sqlstate_errors) { + PyErr_SetString(PyExc_SystemError, + "sqlstate_errors_init(): already called"); + goto exit; + } + if (!(errmodule = PyImport_ImportModule("psycopg2.errors"))) { + goto exit; + } + if (!(sqlstate_errors = PyDict_New())) { + goto exit; + } + Py_INCREF(sqlstate_errors); + if (0 > PyModule_AddObject(module, "sqlstate_errors", sqlstate_errors)) { + Py_DECREF(sqlstate_errors); + return -1; + } + + strcpy(namebuf, prefix); + suffix = namebuf + sizeof(prefix) - 1; + bufsize = sizeof(namebuf) - sizeof(prefix) - 1; + /* If this 0 gets deleted the buffer was too small. */ + namebuf[sizeof(namebuf) - 1] = '\0'; + + for (i = 0; sqlstate_table[i].sqlstate; i++) { + PyObject *base; + + base = base_exception_from_sqlstate(sqlstate_table[i].sqlstate); + strncpy(suffix, sqlstate_table[i].name, bufsize); + if (namebuf[sizeof(namebuf) - 1] != '\0') { + PyErr_SetString( + PyExc_SystemError, "sqlstate_errors_init(): buffer too small"); + goto exit; + } + if (!(exc = PyErr_NewException(namebuf, base, NULL))) { + goto exit; + } + if (0 > PyDict_SetItemString( + sqlstate_errors, sqlstate_table[i].sqlstate, exc)) { + goto exit; + } + if (0 > PyModule_AddObject(errmodule, sqlstate_table[i].name, exc)) { + goto exit; + } + exc = NULL; /* ref stolen by the module */ + } + + rv = 0; + +exit: + Py_XDECREF(errmodule); + Py_XDECREF(exc); + return rv; +} + + RAISES_NEG static int add_module_constants(PyObject *module) { @@ -1012,7 +1104,8 @@ INIT_MODULE(_psycopg)(void) if (0 > encodings_init(module)) { goto exit; } if (0 > typecast_init(module)) { goto exit; } if (0 > adapters_init(module)) { goto exit; } - if (0 > errors_init(module)) { goto exit; } + if (0 > basic_errors_init(module)) { goto exit; } + if (0 > sqlstate_errors_init(module)) { goto exit; } Dprintf("psycopgmodule: module initialization complete"); diff --git a/psycopg/sqlstate_errors.h b/psycopg/sqlstate_errors.h new file mode 100644 index 00000000..ecc6b4f6 --- /dev/null +++ b/psycopg/sqlstate_errors.h @@ -0,0 +1,318 @@ +/* + * Autogenerated by 'scripts/make_errors.py'. + */ + + +/* Class 02 - No Data (this is also a warning class per the SQL standard) */ +{"02000", "NoData"}, +{"02001", "NoAdditionalDynamicResultSetsReturned"}, + +/* Class 03 - SQL Statement Not Yet Complete */ +{"03000", "SqlStatementNotYetComplete"}, + +/* Class 08 - Connection Exception */ +{"08000", "ConnectionException"}, +{"08001", "SqlclientUnableToEstablishSqlconnection"}, +{"08003", "ConnectionDoesNotExist"}, +{"08004", "SqlserverRejectedEstablishmentOfSqlconnection"}, +{"08006", "ConnectionFailure"}, +{"08007", "TransactionResolutionUnknown"}, +{"08P01", "ProtocolViolation"}, + +/* Class 09 - Triggered Action Exception */ +{"09000", "TriggeredActionException"}, + +/* Class 0A - Feature Not Supported */ +{"0A000", "FeatureNotSupported"}, + +/* Class 0B - Invalid Transaction Initiation */ +{"0B000", "InvalidTransactionInitiation"}, + +/* Class 0F - Locator Exception */ +{"0F000", "LocatorException"}, +{"0F001", "InvalidLocatorSpecification"}, + +/* Class 0L - Invalid Grantor */ +{"0L000", "InvalidGrantor"}, +{"0LP01", "InvalidGrantOperation"}, + +/* Class 0P - Invalid Role Specification */ +{"0P000", "InvalidRoleSpecification"}, + +/* Class 0Z - Diagnostics Exception */ +{"0Z000", "DiagnosticsException"}, +{"0Z002", "StackedDiagnosticsAccessedWithoutActiveHandler"}, + +/* Class 20 - Case Not Found */ +{"20000", "CaseNotFound"}, + +/* Class 21 - Cardinality Violation */ +{"21000", "CardinalityViolation"}, + +/* Class 22 - Data Exception */ +{"22000", "DataException"}, +{"22001", "StringDataRightTruncation"}, +{"22002", "NullValueNoIndicatorParameter"}, +{"22003", "NumericValueOutOfRange"}, +{"22004", "NullValueNotAllowed"}, +{"22005", "ErrorInAssignment"}, +{"22007", "InvalidDatetimeFormat"}, +{"22008", "DatetimeFieldOverflow"}, +{"22009", "InvalidTimeZoneDisplacementValue"}, +{"2200B", "EscapeCharacterConflict"}, +{"2200C", "InvalidUseOfEscapeCharacter"}, +{"2200D", "InvalidEscapeOctet"}, +{"2200F", "ZeroLengthCharacterString"}, +{"2200G", "MostSpecificTypeMismatch"}, +{"2200H", "SequenceGeneratorLimitExceeded"}, +{"2200L", "NotAnXmlDocument"}, +{"2200M", "InvalidXmlDocument"}, +{"2200N", "InvalidXmlContent"}, +{"2200S", "InvalidXmlComment"}, +{"2200T", "InvalidXmlProcessingInstruction"}, +{"22010", "InvalidIndicatorParameterValue"}, +{"22011", "SubstringError"}, +{"22012", "DivisionByZero"}, +{"22013", "InvalidPrecedingOrFollowingSize"}, +{"22014", "InvalidArgumentForNtileFunction"}, +{"22015", "IntervalFieldOverflow"}, +{"22016", "InvalidArgumentForNthValueFunction"}, +{"22018", "InvalidCharacterValueForCast"}, +{"22019", "InvalidEscapeCharacter"}, +{"2201B", "InvalidRegularExpression"}, +{"2201E", "InvalidArgumentForLogarithm"}, +{"2201F", "InvalidArgumentForPowerFunction"}, +{"2201G", "InvalidArgumentForWidthBucketFunction"}, +{"2201W", "InvalidRowCountInLimitClause"}, +{"2201X", "InvalidRowCountInResultOffsetClause"}, +{"22021", "CharacterNotInRepertoire"}, +{"22022", "IndicatorOverflow"}, +{"22023", "InvalidParameterValue"}, +{"22024", "UnterminatedCString"}, +{"22025", "InvalidEscapeSequence"}, +{"22026", "StringDataLengthMismatch"}, +{"22027", "TrimError"}, +{"2202E", "ArraySubscriptError"}, +{"2202G", "InvalidTablesampleRepeat"}, +{"2202H", "InvalidTablesampleArgument"}, +{"22P01", "FloatingPointException"}, +{"22P02", "InvalidTextRepresentation"}, +{"22P03", "InvalidBinaryRepresentation"}, +{"22P04", "BadCopyFileFormat"}, +{"22P05", "UntranslatableCharacter"}, +{"22P06", "NonstandardUseOfEscapeCharacter"}, + +/* Class 23 - Integrity Constraint Violation */ +{"23000", "IntegrityConstraintViolation"}, +{"23001", "RestrictViolation"}, +{"23502", "NotNullViolation"}, +{"23503", "ForeignKeyViolation"}, +{"23505", "UniqueViolation"}, +{"23514", "CheckViolation"}, +{"23P01", "ExclusionViolation"}, + +/* Class 24 - Invalid Cursor State */ +{"24000", "InvalidCursorState"}, + +/* Class 25 - Invalid Transaction State */ +{"25000", "InvalidTransactionState"}, +{"25001", "ActiveSqlTransaction"}, +{"25002", "BranchTransactionAlreadyActive"}, +{"25003", "InappropriateAccessModeForBranchTransaction"}, +{"25004", "InappropriateIsolationLevelForBranchTransaction"}, +{"25005", "NoActiveSqlTransactionForBranchTransaction"}, +{"25006", "ReadOnlySqlTransaction"}, +{"25007", "SchemaAndDataStatementMixingNotSupported"}, +{"25008", "HeldCursorRequiresSameIsolationLevel"}, +{"25P01", "NoActiveSqlTransaction"}, +{"25P02", "InFailedSqlTransaction"}, +{"25P03", "IdleInTransactionSessionTimeout"}, + +/* Class 26 - Invalid SQL Statement Name */ +{"26000", "InvalidSqlStatementName"}, + +/* Class 27 - Triggered Data Change Violation */ +{"27000", "TriggeredDataChangeViolation"}, + +/* Class 28 - Invalid Authorization Specification */ +{"28000", "InvalidAuthorizationSpecification"}, +{"28P01", "InvalidPassword"}, + +/* Class 2B - Dependent Privilege Descriptors Still Exist */ +{"2B000", "DependentPrivilegeDescriptorsStillExist"}, +{"2BP01", "DependentObjectsStillExist"}, + +/* Class 2D - Invalid Transaction Termination */ +{"2D000", "InvalidTransactionTermination"}, + +/* Class 2F - SQL Routine Exception */ +{"2F000", "SqlRoutineException"}, +{"2F002", "ModifyingSqlDataNotPermitted"}, +{"2F003", "ProhibitedSqlStatementAttempted"}, +{"2F004", "ReadingSqlDataNotPermitted"}, +{"2F005", "FunctionExecutedNoReturnStatement"}, + +/* Class 34 - Invalid Cursor Name */ +{"34000", "InvalidCursorName"}, + +/* Class 38 - External Routine Exception */ +{"38000", "ExternalRoutineException"}, +{"38001", "ContainingSqlNotPermitted"}, +{"38002", "ModifyingSqlDataNotPermittedExt"}, +{"38003", "ProhibitedSqlStatementAttemptedExt"}, +{"38004", "ReadingSqlDataNotPermittedExt"}, + +/* Class 39 - External Routine Invocation Exception */ +{"39000", "ExternalRoutineInvocationException"}, +{"39001", "InvalidSqlstateReturned"}, +{"39004", "NullValueNotAllowedExt"}, +{"39P01", "TriggerProtocolViolated"}, +{"39P02", "SrfProtocolViolated"}, +{"39P03", "EventTriggerProtocolViolated"}, + +/* Class 3B - Savepoint Exception */ +{"3B000", "SavepointException"}, +{"3B001", "InvalidSavepointSpecification"}, + +/* Class 3D - Invalid Catalog Name */ +{"3D000", "InvalidCatalogName"}, + +/* Class 3F - Invalid Schema Name */ +{"3F000", "InvalidSchemaName"}, + +/* Class 40 - Transaction Rollback */ +{"40000", "TransactionRollback"}, +{"40001", "SerializationFailure"}, +{"40002", "TransactionIntegrityConstraintViolation"}, +{"40003", "StatementCompletionUnknown"}, +{"40P01", "DeadlockDetected"}, + +/* Class 42 - Syntax Error or Access Rule Violation */ +{"42000", "SyntaxErrorOrAccessRuleViolation"}, +{"42501", "InsufficientPrivilege"}, +{"42601", "SyntaxError"}, +{"42602", "InvalidName"}, +{"42611", "InvalidColumnDefinition"}, +{"42622", "NameTooLong"}, +{"42701", "DuplicateColumn"}, +{"42702", "AmbiguousColumn"}, +{"42703", "UndefinedColumn"}, +{"42704", "UndefinedObject"}, +{"42710", "DuplicateObject"}, +{"42712", "DuplicateAlias"}, +{"42723", "DuplicateFunction"}, +{"42725", "AmbiguousFunction"}, +{"42803", "GroupingError"}, +{"42804", "DatatypeMismatch"}, +{"42809", "WrongObjectType"}, +{"42830", "InvalidForeignKey"}, +{"42846", "CannotCoerce"}, +{"42883", "UndefinedFunction"}, +{"428C9", "GeneratedAlways"}, +{"42939", "ReservedName"}, +{"42P01", "UndefinedTable"}, +{"42P02", "UndefinedParameter"}, +{"42P03", "DuplicateCursor"}, +{"42P04", "DuplicateDatabase"}, +{"42P05", "DuplicatePreparedStatement"}, +{"42P06", "DuplicateSchema"}, +{"42P07", "DuplicateTable"}, +{"42P08", "AmbiguousParameter"}, +{"42P09", "AmbiguousAlias"}, +{"42P10", "InvalidColumnReference"}, +{"42P11", "InvalidCursorDefinition"}, +{"42P12", "InvalidDatabaseDefinition"}, +{"42P13", "InvalidFunctionDefinition"}, +{"42P14", "InvalidPreparedStatementDefinition"}, +{"42P15", "InvalidSchemaDefinition"}, +{"42P16", "InvalidTableDefinition"}, +{"42P17", "InvalidObjectDefinition"}, +{"42P18", "IndeterminateDatatype"}, +{"42P19", "InvalidRecursion"}, +{"42P20", "WindowingError"}, +{"42P21", "CollationMismatch"}, +{"42P22", "IndeterminateCollation"}, + +/* Class 44 - WITH CHECK OPTION Violation */ +{"44000", "WithCheckOptionViolation"}, + +/* Class 53 - Insufficient Resources */ +{"53000", "InsufficientResources"}, +{"53100", "DiskFull"}, +{"53200", "OutOfMemory"}, +{"53300", "TooManyConnections"}, +{"53400", "ConfigurationLimitExceeded"}, + +/* Class 54 - Program Limit Exceeded */ +{"54000", "ProgramLimitExceeded"}, +{"54001", "StatementTooComplex"}, +{"54011", "TooManyColumns"}, +{"54023", "TooManyArguments"}, + +/* Class 55 - Object Not In Prerequisite State */ +{"55000", "ObjectNotInPrerequisiteState"}, +{"55006", "ObjectInUse"}, +{"55P02", "CantChangeRuntimeParam"}, +{"55P03", "LockNotAvailable"}, + +/* Class 57 - Operator Intervention */ +{"57000", "OperatorIntervention"}, +{"57014", "QueryCanceled"}, +{"57P01", "AdminShutdown"}, +{"57P02", "CrashShutdown"}, +{"57P03", "CannotConnectNow"}, +{"57P04", "DatabaseDropped"}, + +/* Class 58 - System Error (errors external to PostgreSQL itself) */ +{"58000", "SystemError"}, +{"58030", "IoError"}, +{"58P01", "UndefinedFile"}, +{"58P02", "DuplicateFile"}, + +/* Class 72 - Snapshot Failure */ +{"72000", "SnapshotTooOld"}, + +/* Class F0 - Configuration File Error */ +{"F0000", "ConfigFileError"}, +{"F0001", "LockFileExists"}, + +/* Class HV - Foreign Data Wrapper Error (SQL/MED) */ +{"HV000", "FdwError"}, +{"HV001", "FdwOutOfMemory"}, +{"HV002", "FdwDynamicParameterValueNeeded"}, +{"HV004", "FdwInvalidDataType"}, +{"HV005", "FdwColumnNameNotFound"}, +{"HV006", "FdwInvalidDataTypeDescriptors"}, +{"HV007", "FdwInvalidColumnName"}, +{"HV008", "FdwInvalidColumnNumber"}, +{"HV009", "FdwInvalidUseOfNullPointer"}, +{"HV00A", "FdwInvalidStringFormat"}, +{"HV00B", "FdwInvalidHandle"}, +{"HV00C", "FdwInvalidOptionIndex"}, +{"HV00D", "FdwInvalidOptionName"}, +{"HV00J", "FdwOptionNameNotFound"}, +{"HV00K", "FdwReplyHandle"}, +{"HV00L", "FdwUnableToCreateExecution"}, +{"HV00M", "FdwUnableToCreateReply"}, +{"HV00N", "FdwUnableToEstablishConnection"}, +{"HV00P", "FdwNoSchemas"}, +{"HV00Q", "FdwSchemaNotFound"}, +{"HV00R", "FdwTableNotFound"}, +{"HV010", "FdwFunctionSequenceError"}, +{"HV014", "FdwTooManyHandles"}, +{"HV021", "FdwInconsistentDescriptorInformation"}, +{"HV024", "FdwInvalidAttributeValue"}, +{"HV090", "FdwInvalidStringLengthOrBufferLength"}, +{"HV091", "FdwInvalidDescriptorFieldIdentifier"}, + +/* Class P0 - PL/pgSQL Error */ +{"P0000", "PlpgsqlError"}, +{"P0001", "RaiseException"}, +{"P0002", "NoDataFound"}, +{"P0003", "TooManyRows"}, +{"P0004", "AssertFailure"}, + +/* Class XX - Internal Error */ +{"XX000", "InternalError_"}, +{"XX001", "DataCorrupted"}, +{"XX002", "IndexCorrupted"}, diff --git a/scripts/make_errors.py b/scripts/make_errors.py index 6a59cf1a..8e5f4f38 100755 --- a/scripts/make_errors.py +++ b/scripts/make_errors.py @@ -17,6 +17,7 @@ The script can be run at a new PostgreSQL release to refresh the module. # License for more details. from __future__ import print_function +import os import re import sys import urllib2 @@ -24,34 +25,19 @@ from collections import defaultdict def main(): - if len(sys.argv) != 2: - print("usage: %s /path/to/errors.py" % sys.argv[0], file=sys.stderr) - return 2 + filename = os.path.join( + os.path.dirname(__file__), "../psycopg/sqlstate_errors.h") - filename = sys.argv[1] - - file_start = read_base_file(filename) # If you add a version to the list fix the docs (in errors.rst) classes, errors = fetch_errors( ['9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '10', '11']) f = open(filename, "w") - for line in file_start: - print(line, file=f) + print("/*\n * Autogenerated by 'scripts/make_errors.py'.\n */\n", file=f) for line in generate_module_data(classes, errors): print(line, file=f) -def read_base_file(filename): - rv = [] - for line in open(filename): - rv.append(line.rstrip("\n")) - if line.startswith("# autogenerated"): - return rv - - raise ValueError("can't find the separator. Is this the right file?") - - def parse_errors_txt(url): classes = {} errors = defaultdict(dict) @@ -112,12 +98,7 @@ def fetch_errors(versions): def generate_module_data(classes, errors): - tmpl = """ - -@for_sqlstate(%(errcode)r) -class %(cls)s(%(base)s): - pass\ -""" + tmpl = '{"%(errcode)s", "%(cls)s"},' specific = { '38002': 'ModifyingSqlDataNotPermittedExt', '38003': 'ProhibitedSqlStatementAttemptedExt', @@ -137,7 +118,7 @@ class %(cls)s(%(base)s): # success and warning - never raised continue - yield "\n\n# %s" % clslabel + yield "\n/* %s */" % clslabel for errcode, errlabel in sorted(errors[clscode].items()): if errcode in specific: @@ -150,81 +131,9 @@ class %(cls)s(%(base)s): yield tmpl % { 'cls': clsname, - 'base': get_base_class_name(errcode), 'errcode': errcode } -def get_base_class_name(errcode): - """ - This is a python porting of exception_from_sqlstate code in pqpath.c - """ - if errcode[0] == '0': - if errcode[1] == 'A': # Class 0A - Feature Not Supported - return 'NotSupportedError' - elif errcode[0] == '2': - if errcode[1] in '01': - # Class 20 - Case Not Found - # Class 21 - Cardinality Violation - return 'ProgrammingError' - elif errcode[1] == '2': # Class 22 - Data Exception - return 'DataError' - elif errcode[1] == '3': # Class 23 - Integrity Constraint Violation - return 'IntegrityError' - elif errcode[1] in '45': - # Class 24 - Invalid Cursor State - # Class 25 - Invalid Transaction State - return 'InternalError' - elif errcode[1] in '678': - # Class 26 - Invalid SQL Statement Name - # Class 27 - Triggered Data Change Violation - # Class 28 - Invalid Authorization Specification - return 'OperationalError' - elif errcode[1] in 'BDF': - # Class 2B - Dependent Privilege Descriptors Still Exist - # Class 2D - Invalid Transaction Termination - # Class 2F - SQL Routine Exception - return 'InternalError' - elif errcode[0] == '3': - if errcode[1] == '4': # Class 34 - Invalid Cursor Name - return 'OperationalError' - if errcode[1] in '89B': - # Class 38 - External Routine Exception - # Class 39 - External Routine Invocation Exception - # Class 3B - Savepoint Exception - return 'InternalError' - if errcode[1] in 'DF': - # Class 3D - Invalid Catalog Name - # Class 3F - Invalid Schema Name - return 'ProgrammingError' - elif errcode[0] == '4': - if errcode[1] == '0': # Class 40 - Transaction Rollback - return 'TransactionRollbackError' - if errcode[1] in '24': - # Class 42 - Syntax Error or Access Rule Violation - # Class 44 - WITH CHECK OPTION Violation - return 'ProgrammingError' - elif errcode[0] == '5': - if errcode == "57014": - return 'QueryCanceledError' - # Class 53 - Insufficient Resources - # Class 54 - Program Limit Exceeded - # Class 55 - Object Not In Prerequisite State - # Class 57 - Operator Intervention - # Class 58 - System Error (errors external to PostgreSQL itself) - else: - return 'OperationalError' - elif errcode[0] == 'F': # Class F0 - Configuration File Error - return 'InternalError' - elif errcode[0] == 'H': # Class HV - Foreign Data Wrapper Error (SQL/MED) - return 'OperationalError' - elif errcode[0] == 'P': # Class P0 - PL/pgSQL Error - return 'InternalError' - elif errcode[0] == 'X': # Class XX - Internal Error - return 'InternalError' - - return 'DatabaseError' - - if __name__ == '__main__': sys.exit(main()) diff --git a/setup.py b/setup.py index 2c3be246..ce234a37 100644 --- a/setup.py +++ b/setup.py @@ -515,7 +515,7 @@ depends = [ 'adapter_list.h', 'adapter_pboolean.h', 'adapter_pdecimal.h', 'adapter_pint.h', 'adapter_pfloat.h', 'adapter_qstring.h', 'microprotocols.h', 'microprotocols_proto.h', - 'typecast.h', 'typecast_binary.h', + 'typecast.h', 'typecast_binary.h', 'sqlstate_errors.h', # included sources 'typecast_array.c', 'typecast_basic.c', 'typecast_binary.c', diff --git a/tests/test_errors.py b/tests/test_errors.py index f67bb721..1f11a60a 100755 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -43,14 +43,14 @@ class ErrorsTests(ConnectingTestCase): def test_exception_class_fallback(self): cur = self.conn.cursor() - from psycopg2 import errors - x = errors._by_sqlstate.pop('42P01') + from psycopg2._psycopg import sqlstate_errors + x = sqlstate_errors.pop('42P01') try: cur.execute("select * from nonexist") except psycopg2.Error as exc: e = exc finally: - errors._by_sqlstate['42P01'] = x + sqlstate_errors['42P01'] = x self.assertEqual(type(e), self.conn.ProgrammingError) @@ -62,6 +62,25 @@ class ErrorsTests(ConnectingTestCase): with self.assertRaises(KeyError): errors.lookup('XXXXX') + def test_has_base_exceptions(self): + import psycopg2 + from psycopg2 import errors + + excs = [] + for n in dir(psycopg2): + obj = getattr(psycopg2, n) + if isinstance(obj, type) and issubclass(obj, StandardError): + excs.append(obj) + + self.assert_(len(excs) > 8, str(excs)) + + excs.append(psycopg2.extensions.QueryCanceledError) + excs.append(psycopg2.extensions.TransactionRollbackError) + + for exc in excs: + self.assert_(hasattr(errors, exc.__name__)) + self.assert_(getattr(errors, exc.__name__) is exc) + def test_suite(): return unittest.TestLoader().loadTestsFromName(__name__) From 99f680b6feb040684a71b372db6a39b54106d392 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Sun, 10 Feb 2019 04:25:06 +0000 Subject: [PATCH 2/4] Allow importing _psycopg even if the 'errors' module is not available --- psycopg/psycopgmodule.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index 122d275b..a8b0dc1a 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -711,7 +711,8 @@ basic_errors_init(PyObject *module) } if (!(errmodule = PyImport_ImportModule("psycopg2.errors"))) { - goto exit; + /* don't inject the exceptions into the errors module */ + PyErr_Clear(); } for (i = 0; exctable[i].name; i++) { @@ -727,10 +728,12 @@ basic_errors_init(PyObject *module) Py_DECREF(*exctable[i].exc); goto exit; } - Py_INCREF(*exctable[i].exc); - if (0 > PyModule_AddObject(errmodule, name, *exctable[i].exc)) { - Py_DECREF(*exctable[i].exc); - goto exit; + if (errmodule) { + Py_INCREF(*exctable[i].exc); + if (0 > PyModule_AddObject(errmodule, name, *exctable[i].exc)) { + Py_DECREF(*exctable[i].exc); + goto exit; + } } } @@ -774,7 +777,8 @@ sqlstate_errors_init(PyObject *module) goto exit; } if (!(errmodule = PyImport_ImportModule("psycopg2.errors"))) { - goto exit; + /* don't inject the exceptions into the errors module */ + PyErr_Clear(); } if (!(sqlstate_errors = PyDict_New())) { goto exit; @@ -808,10 +812,20 @@ sqlstate_errors_init(PyObject *module) sqlstate_errors, sqlstate_table[i].sqlstate, exc)) { goto exit; } - if (0 > PyModule_AddObject(errmodule, sqlstate_table[i].name, exc)) { - goto exit; + + /* Expose the exceptions to psycopg2.errors */ + if (errmodule) { + if (0 > PyModule_AddObject( + errmodule, sqlstate_table[i].name, exc)) { + goto exit; + } + else { + exc = NULL; /* ref stolen by the module */ + } + } + else { + Py_CLEAR(exc); } - exc = NULL; /* ref stolen by the module */ } rv = 0; From 3de4d17519670f5db058ef98c7b80a11b5ea23ea Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Mon, 11 Feb 2019 00:26:01 +0000 Subject: [PATCH 3/4] Fixed use of StandardError in test In Py3 it's gone. --- tests/test_errors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_errors.py b/tests/test_errors.py index 1f11a60a..e8c61a8d 100755 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -69,7 +69,7 @@ class ErrorsTests(ConnectingTestCase): excs = [] for n in dir(psycopg2): obj = getattr(psycopg2, n) - if isinstance(obj, type) and issubclass(obj, StandardError): + if isinstance(obj, type) and issubclass(obj, Exception): excs.append(obj) self.assert_(len(excs) > 8, str(excs)) From 7c148ecee43ae2a9045d97f1451ee92beb827784 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Mon, 11 Feb 2019 01:09:20 +0000 Subject: [PATCH 4/4] Improvements to errors module docs --- doc/src/errors.rst | 19 ++++++++++++++----- doc/src/module.rst | 5 +++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/doc/src/errors.rst b/doc/src/errors.rst index 061999b1..d8f4bd4a 100644 --- a/doc/src/errors.rst +++ b/doc/src/errors.rst @@ -10,8 +10,9 @@ .. versionadded:: 2.8 -This module contains the classes psycopg raises upon receiving an error from -the database with a :sql:`SQLSTATE` value attached. The module is generated +This module exposes the classes psycopg raises upon receiving an error from +the database with a :sql:`SQLSTATE` value attached (available in the +`~psycopg2.Error.pgcode` attribute). The content of the module is generated from the PostgreSQL source code and includes classes for every error defined by PostgreSQL in versions between 9.1 and 11. @@ -53,8 +54,16 @@ idiomatic error handler: except psycopg2.errors.LockNotAvailable: locked = True -For completeness, the module also exposes all the DB-API-defined classes and -:ref:`a few psycopg-specific exceptions ` previously -exposed by the `!extensions` module. One stop shop for all your mistakes... +For completeness, the module also exposes all the :ref:`DB-API-defined +exceptions ` and :ref:`a few psycopg-specific ones +` exposed by the `!extensions` module. One stop shop +for all your mistakes... .. autofunction:: lookup + + .. code-block:: python + + try: + cur.execute("LOCK TABLE mytable IN ACCESS EXCLUSIVE MODE NOWAIT") + except psycopg2.errors.lookup("55P03"): + locked = True diff --git a/doc/src/module.rst b/doc/src/module.rst index c6944f8f..f17f3ae4 100644 --- a/doc/src/module.rst +++ b/doc/src/module.rst @@ -137,14 +137,15 @@ available through the following exceptions: .. exception:: Warning Exception raised for important warnings like data truncations while - inserting, etc. It is a subclass of the Python `~exceptions.StandardError`. + inserting, etc. It is a subclass of the Python `StandardError` + (`Exception` on Python 3). .. exception:: Error Exception that is the base class of all other error exceptions. You can use this to catch all errors with one single `!except` statement. Warnings are not considered errors and thus not use this class as base. It - is a subclass of the Python `!StandardError`. + is a subclass of the Python `StandardError` (`Exception` on Python 3). .. attribute:: pgerror