Merge remote-tracking branch 'origin/errors-module-c'

This commit is contained in:
Daniele Varrazzo 2019-02-16 13:05:20 +01:00
commit 16b35ac77b
12 changed files with 602 additions and 1537 deletions

View File

@ -10,8 +10,9 @@
.. versionadded:: 2.8 .. versionadded:: 2.8
This module contains the classes psycopg raises upon receiving an error from This module exposes the classes psycopg raises upon receiving an error from
the database with a :sql:`SQLSTATE` value attached. The module is generated 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 from the PostgreSQL source code and includes classes for every error defined
by PostgreSQL in versions between 9.1 and 11. by PostgreSQL in versions between 9.1 and 11.
@ -53,8 +54,16 @@ idiomatic error handler:
except psycopg2.errors.LockNotAvailable: except psycopg2.errors.LockNotAvailable:
locked = True locked = True
For completeness, the module also exposes all the DB-API-defined classes and For completeness, the module also exposes all the :ref:`DB-API-defined
:ref:`a few psycopg-specific exceptions <extension-exceptions>` previously exceptions <dbapi-exceptions>` and :ref:`a few psycopg-specific ones
exposed by the `!extensions` module. One stop shop for all your mistakes... <extension-exceptions>` exposed by the `!extensions` module. One stop shop
for all your mistakes...
.. autofunction:: lookup .. autofunction:: lookup
.. code-block:: python
try:
cur.execute("LOCK TABLE mytable IN ACCESS EXCLUSIVE MODE NOWAIT")
except psycopg2.errors.lookup("55P03"):
locked = True

View File

@ -137,14 +137,15 @@ available through the following exceptions:
.. exception:: Warning .. exception:: Warning
Exception raised for important warnings like data truncations while 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:: Error
Exception that is the base class of all other error exceptions. You can 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 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 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 .. attribute:: pgerror

File diff suppressed because it is too large Load Diff

View File

@ -39,5 +39,7 @@ typedef struct {
} errorObject; } errorObject;
HIDDEN PyObject *error_text_from_chars(errorObject *self, const char *str); 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 */ #endif /* PSYCOPG_ERROR_H */

View File

@ -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[] = static const char pgerror_doc[] =
"The error message returned by the backend, if available, else None"; "The error message returned by the backend, if available, else None";

View File

@ -77,130 +77,6 @@ strip_severity(const char *msg)
return 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 /* pq_raise - raise a python exception of the right kind

View File

@ -53,6 +53,9 @@ extern HIDDEN PyObject *Error, *Warning, *InterfaceError, *DatabaseError,
*IntegrityError, *DataError, *NotSupportedError; *IntegrityError, *DataError, *NotSupportedError;
extern HIDDEN PyObject *QueryCanceledError, *TransactionRollbackError; extern HIDDEN PyObject *QueryCanceledError, *TransactionRollbackError;
/* sqlstate -> exception map */
extern HIDDEN PyObject *sqlstate_errors;
/* postgresql<->python encoding map */ /* postgresql<->python encoding map */
extern HIDDEN PyObject *psycoEncodings; extern HIDDEN PyObject *psycoEncodings;

View File

@ -62,6 +62,8 @@
#include "psycopg/adapter_datetime.h" #include "psycopg/adapter_datetime.h"
HIDDEN PyObject *psycoEncodings = NULL; HIDDEN PyObject *psycoEncodings = NULL;
HIDDEN PyObject *sqlstate_errors = NULL;
#ifdef PSYCOPG_DEBUG #ifdef PSYCOPG_DEBUG
HIDDEN int psycopg_debug_enabled = 0; HIDDEN int psycopg_debug_enabled = 0;
#endif #endif
@ -671,7 +673,7 @@ static struct {
RAISES_NEG static int RAISES_NEG static int
errors_init(PyObject *module) basic_errors_init(PyObject *module)
{ {
/* the names of the exceptions here reflect the organization of the /* the names of the exceptions here reflect the organization of the
psycopg2 module and not the fact the the original error objects psycopg2 module and not the fact the the original error objects
@ -680,6 +682,7 @@ errors_init(PyObject *module)
int i; int i;
PyObject *dict = NULL; PyObject *dict = NULL;
PyObject *str = NULL; PyObject *str = NULL;
PyObject *errmodule = NULL;
int rv = -1; int rv = -1;
Dprintf("psycopgmodule: initializing basic exceptions"); Dprintf("psycopgmodule: initializing basic exceptions");
@ -707,6 +710,11 @@ errors_init(PyObject *module)
Py_CLEAR(dict); Py_CLEAR(dict);
} }
if (!(errmodule = PyImport_ImportModule("psycopg2.errors"))) {
/* don't inject the exceptions into the errors module */
PyErr_Clear();
}
for (i = 0; exctable[i].name; i++) { for (i = 0; exctable[i].name; i++) {
char *name; char *name;
if (NULL == exctable[i].exc) { continue; } if (NULL == exctable[i].exc) { continue; }
@ -720,17 +728,115 @@ errors_init(PyObject *module)
Py_DECREF(*exctable[i].exc); Py_DECREF(*exctable[i].exc);
goto exit; 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;
}
}
} }
rv = 0; rv = 0;
exit: exit:
Py_XDECREF(errmodule);
Py_XDECREF(str); Py_XDECREF(str);
Py_XDECREF(dict); Py_XDECREF(dict);
return rv; 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"))) {
/* don't inject the exceptions into the errors module */
PyErr_Clear();
}
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;
}
/* 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);
}
}
rv = 0;
exit:
Py_XDECREF(errmodule);
Py_XDECREF(exc);
return rv;
}
RAISES_NEG static int RAISES_NEG static int
add_module_constants(PyObject *module) add_module_constants(PyObject *module)
{ {
@ -1012,7 +1118,8 @@ INIT_MODULE(_psycopg)(void)
if (0 > encodings_init(module)) { goto exit; } if (0 > encodings_init(module)) { goto exit; }
if (0 > typecast_init(module)) { goto exit; } if (0 > typecast_init(module)) { goto exit; }
if (0 > adapters_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"); Dprintf("psycopgmodule: module initialization complete");

318
psycopg/sqlstate_errors.h Normal file
View File

@ -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"},

View File

@ -17,6 +17,7 @@ The script can be run at a new PostgreSQL release to refresh the module.
# License for more details. # License for more details.
from __future__ import print_function from __future__ import print_function
import os
import re import re
import sys import sys
import urllib2 import urllib2
@ -24,34 +25,19 @@ from collections import defaultdict
def main(): def main():
if len(sys.argv) != 2: filename = os.path.join(
print("usage: %s /path/to/errors.py" % sys.argv[0], file=sys.stderr) os.path.dirname(__file__), "../psycopg/sqlstate_errors.h")
return 2
filename = sys.argv[1]
file_start = read_base_file(filename)
# If you add a version to the list fix the docs (in errors.rst) # If you add a version to the list fix the docs (in errors.rst)
classes, errors = fetch_errors( classes, errors = fetch_errors(
['9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '10', '11']) ['9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '10', '11'])
f = open(filename, "w") f = open(filename, "w")
for line in file_start: print("/*\n * Autogenerated by 'scripts/make_errors.py'.\n */\n", file=f)
print(line, file=f)
for line in generate_module_data(classes, errors): for line in generate_module_data(classes, errors):
print(line, file=f) 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): def parse_errors_txt(url):
classes = {} classes = {}
errors = defaultdict(dict) errors = defaultdict(dict)
@ -112,12 +98,7 @@ def fetch_errors(versions):
def generate_module_data(classes, errors): def generate_module_data(classes, errors):
tmpl = """ tmpl = '{"%(errcode)s", "%(cls)s"},'
@for_sqlstate(%(errcode)r)
class %(cls)s(%(base)s):
pass\
"""
specific = { specific = {
'38002': 'ModifyingSqlDataNotPermittedExt', '38002': 'ModifyingSqlDataNotPermittedExt',
'38003': 'ProhibitedSqlStatementAttemptedExt', '38003': 'ProhibitedSqlStatementAttemptedExt',
@ -137,7 +118,7 @@ class %(cls)s(%(base)s):
# success and warning - never raised # success and warning - never raised
continue continue
yield "\n\n# %s" % clslabel yield "\n/* %s */" % clslabel
for errcode, errlabel in sorted(errors[clscode].items()): for errcode, errlabel in sorted(errors[clscode].items()):
if errcode in specific: if errcode in specific:
@ -150,81 +131,9 @@ class %(cls)s(%(base)s):
yield tmpl % { yield tmpl % {
'cls': clsname, 'cls': clsname,
'base': get_base_class_name(errcode),
'errcode': 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__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main())

View File

@ -515,7 +515,7 @@ depends = [
'adapter_list.h', 'adapter_pboolean.h', 'adapter_pdecimal.h', 'adapter_list.h', 'adapter_pboolean.h', 'adapter_pdecimal.h',
'adapter_pint.h', 'adapter_pfloat.h', 'adapter_qstring.h', 'adapter_pint.h', 'adapter_pfloat.h', 'adapter_qstring.h',
'microprotocols.h', 'microprotocols_proto.h', 'microprotocols.h', 'microprotocols_proto.h',
'typecast.h', 'typecast_binary.h', 'typecast.h', 'typecast_binary.h', 'sqlstate_errors.h',
# included sources # included sources
'typecast_array.c', 'typecast_basic.c', 'typecast_binary.c', 'typecast_array.c', 'typecast_basic.c', 'typecast_binary.c',

View File

@ -43,14 +43,14 @@ class ErrorsTests(ConnectingTestCase):
def test_exception_class_fallback(self): def test_exception_class_fallback(self):
cur = self.conn.cursor() cur = self.conn.cursor()
from psycopg2 import errors from psycopg2._psycopg import sqlstate_errors
x = errors._by_sqlstate.pop('42P01') x = sqlstate_errors.pop('42P01')
try: try:
cur.execute("select * from nonexist") cur.execute("select * from nonexist")
except psycopg2.Error as exc: except psycopg2.Error as exc:
e = exc e = exc
finally: finally:
errors._by_sqlstate['42P01'] = x sqlstate_errors['42P01'] = x
self.assertEqual(type(e), self.conn.ProgrammingError) self.assertEqual(type(e), self.conn.ProgrammingError)
@ -62,6 +62,25 @@ class ErrorsTests(ConnectingTestCase):
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
errors.lookup('XXXXX') 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, Exception):
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(): def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__) return unittest.TestLoader().loadTestsFromName(__name__)