Fixed #12702 -- Introduced a common implementation of DatabaseError and IntegrityError, so that database backends can (re)raise common error classes. Thanks for Waldemar Kornewald for the report.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12352 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
47acb1d659
commit
11ee9746a0
|
@ -1,7 +1,8 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import signals
|
from django.core import signals
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.db.utils import ConnectionHandler, ConnectionRouter, load_backend, DEFAULT_DB_ALIAS
|
from django.db.utils import ConnectionHandler, ConnectionRouter, load_backend, DEFAULT_DB_ALIAS, \
|
||||||
|
DatabaseError, IntegrityError
|
||||||
from django.utils.functional import curry
|
from django.utils.functional import curry
|
||||||
|
|
||||||
__all__ = ('backend', 'connection', 'connections', 'router', 'DatabaseError',
|
__all__ = ('backend', 'connection', 'connections', 'router', 'DatabaseError',
|
||||||
|
@ -73,8 +74,6 @@ router = ConnectionRouter(settings.DATABASE_ROUTERS)
|
||||||
# connections['default'] instead.
|
# connections['default'] instead.
|
||||||
connection = connections[DEFAULT_DB_ALIAS]
|
connection = connections[DEFAULT_DB_ALIAS]
|
||||||
backend = load_backend(connection.settings_dict['ENGINE'])
|
backend = load_backend(connection.settings_dict['ENGINE'])
|
||||||
DatabaseError = backend.DatabaseError
|
|
||||||
IntegrityError = backend.IntegrityError
|
|
||||||
|
|
||||||
# Register an event that closes the database connection
|
# Register an event that closes the database connection
|
||||||
# when a Django request is finished.
|
# when a Django request is finished.
|
||||||
|
|
|
@ -5,6 +5,7 @@ Requires MySQLdb: http://sourceforge.net/projects/mysql-python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import MySQLdb as Database
|
import MySQLdb as Database
|
||||||
|
@ -24,6 +25,7 @@ if (version < (1,2,1) or (version[:3] == (1, 2, 1) and
|
||||||
from MySQLdb.converters import conversions
|
from MySQLdb.converters import conversions
|
||||||
from MySQLdb.constants import FIELD_TYPE, FLAG, CLIENT
|
from MySQLdb.constants import FIELD_TYPE, FLAG, CLIENT
|
||||||
|
|
||||||
|
from django.db import utils
|
||||||
from django.db.backends import *
|
from django.db.backends import *
|
||||||
from django.db.backends.signals import connection_created
|
from django.db.backends.signals import connection_created
|
||||||
from django.db.backends.mysql.client import DatabaseClient
|
from django.db.backends.mysql.client import DatabaseClient
|
||||||
|
@ -82,22 +84,30 @@ class CursorWrapper(object):
|
||||||
def execute(self, query, args=None):
|
def execute(self, query, args=None):
|
||||||
try:
|
try:
|
||||||
return self.cursor.execute(query, args)
|
return self.cursor.execute(query, args)
|
||||||
|
except Database.IntegrityError, e:
|
||||||
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
except Database.OperationalError, e:
|
except Database.OperationalError, e:
|
||||||
# Map some error codes to IntegrityError, since they seem to be
|
# Map some error codes to IntegrityError, since they seem to be
|
||||||
# misclassified and Django would prefer the more logical place.
|
# misclassified and Django would prefer the more logical place.
|
||||||
if e[0] in self.codes_for_integrityerror:
|
if e[0] in self.codes_for_integrityerror:
|
||||||
raise Database.IntegrityError(tuple(e))
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
raise
|
raise
|
||||||
|
except Database.DatabaseError, e:
|
||||||
|
raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
|
||||||
def executemany(self, query, args):
|
def executemany(self, query, args):
|
||||||
try:
|
try:
|
||||||
return self.cursor.executemany(query, args)
|
return self.cursor.executemany(query, args)
|
||||||
|
except Database.IntegrityError, e:
|
||||||
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
except Database.OperationalError, e:
|
except Database.OperationalError, e:
|
||||||
# Map some error codes to IntegrityError, since they seem to be
|
# Map some error codes to IntegrityError, since they seem to be
|
||||||
# misclassified and Django would prefer the more logical place.
|
# misclassified and Django would prefer the more logical place.
|
||||||
if e[0] in self.codes_for_integrityerror:
|
if e[0] in self.codes_for_integrityerror:
|
||||||
raise Database.IntegrityError(tuple(e))
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
raise
|
raise
|
||||||
|
except Database.DatabaseError, e:
|
||||||
|
raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if attr in self.__dict__:
|
if attr in self.__dict__:
|
||||||
|
|
|
@ -4,8 +4,10 @@ Oracle database backend for Django.
|
||||||
Requires cx_Oracle: http://cx-oracle.sourceforge.net/
|
Requires cx_Oracle: http://cx-oracle.sourceforge.net/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
try:
|
try:
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
@ -24,6 +26,7 @@ except ImportError, e:
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
raise ImproperlyConfigured("Error loading cx_Oracle module: %s" % e)
|
raise ImproperlyConfigured("Error loading cx_Oracle module: %s" % e)
|
||||||
|
|
||||||
|
from django.db import utils
|
||||||
from django.db.backends import *
|
from django.db.backends import *
|
||||||
from django.db.backends.signals import connection_created
|
from django.db.backends.signals import connection_created
|
||||||
from django.db.backends.oracle.client import DatabaseClient
|
from django.db.backends.oracle.client import DatabaseClient
|
||||||
|
@ -480,11 +483,13 @@ class FormatStylePlaceholderCursor(object):
|
||||||
self._guess_input_sizes([params])
|
self._guess_input_sizes([params])
|
||||||
try:
|
try:
|
||||||
return self.cursor.execute(query, self._param_generator(params))
|
return self.cursor.execute(query, self._param_generator(params))
|
||||||
except DatabaseError, e:
|
except Database.IntegrityError, e:
|
||||||
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
except Database.DatabaseError, e:
|
||||||
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
||||||
if e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
if e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
||||||
e = IntegrityError(e.args[0])
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
raise e
|
raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
|
||||||
def executemany(self, query, params=None):
|
def executemany(self, query, params=None):
|
||||||
try:
|
try:
|
||||||
|
@ -504,11 +509,13 @@ class FormatStylePlaceholderCursor(object):
|
||||||
try:
|
try:
|
||||||
return self.cursor.executemany(query,
|
return self.cursor.executemany(query,
|
||||||
[self._param_generator(p) for p in formatted])
|
[self._param_generator(p) for p in formatted])
|
||||||
except DatabaseError, e:
|
except Database.IntegrityError, e:
|
||||||
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
except Database.DatabaseError, e:
|
||||||
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
||||||
if e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
if e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
||||||
e = IntegrityError(e.args[0])
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
raise e
|
raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
|
||||||
def fetchone(self):
|
def fetchone(self):
|
||||||
row = self.cursor.fetchone()
|
row = self.cursor.fetchone()
|
||||||
|
|
|
@ -4,6 +4,9 @@ PostgreSQL database backend for Django.
|
||||||
Requires psycopg 1: http://initd.org/projects/psycopg1
|
Requires psycopg 1: http://initd.org/projects/psycopg1
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from django.db import utils
|
||||||
from django.db.backends import *
|
from django.db.backends import *
|
||||||
from django.db.backends.signals import connection_created
|
from django.db.backends.signals import connection_created
|
||||||
from django.db.backends.postgresql.client import DatabaseClient
|
from django.db.backends.postgresql.client import DatabaseClient
|
||||||
|
@ -50,11 +53,21 @@ class UnicodeCursorWrapper(object):
|
||||||
return tuple([smart_str(p, self.charset, True) for p in params])
|
return tuple([smart_str(p, self.charset, True) for p in params])
|
||||||
|
|
||||||
def execute(self, sql, params=()):
|
def execute(self, sql, params=()):
|
||||||
return self.cursor.execute(smart_str(sql, self.charset), self.format_params(params))
|
try:
|
||||||
|
return self.cursor.execute(query, args)
|
||||||
|
except Database.IntegrityError, e:
|
||||||
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
except Database.DatabaseError, e:
|
||||||
|
raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
|
||||||
def executemany(self, sql, param_list):
|
def executemany(self, sql, param_list):
|
||||||
new_param_list = [self.format_params(params) for params in param_list]
|
try:
|
||||||
return self.cursor.executemany(sql, new_param_list)
|
new_param_list = [self.format_params(params) for params in param_list]
|
||||||
|
return self.cursor.executemany(sql, new_param_list)
|
||||||
|
except Database.IntegrityError, e:
|
||||||
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
except Database.DatabaseError, e:
|
||||||
|
raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if attr in self.__dict__:
|
if attr in self.__dict__:
|
||||||
|
|
|
@ -4,6 +4,9 @@ PostgreSQL database backend for Django.
|
||||||
Requires psycopg 2: http://initd.org/projects/psycopg2
|
Requires psycopg 2: http://initd.org/projects/psycopg2
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from django.db import utils
|
||||||
from django.db.backends import *
|
from django.db.backends import *
|
||||||
from django.db.backends.signals import connection_created
|
from django.db.backends.signals import connection_created
|
||||||
from django.db.backends.postgresql.operations import DatabaseOperations as PostgresqlDatabaseOperations
|
from django.db.backends.postgresql.operations import DatabaseOperations as PostgresqlDatabaseOperations
|
||||||
|
@ -27,6 +30,40 @@ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||||
psycopg2.extensions.register_adapter(SafeString, psycopg2.extensions.QuotedString)
|
psycopg2.extensions.register_adapter(SafeString, psycopg2.extensions.QuotedString)
|
||||||
psycopg2.extensions.register_adapter(SafeUnicode, psycopg2.extensions.QuotedString)
|
psycopg2.extensions.register_adapter(SafeUnicode, psycopg2.extensions.QuotedString)
|
||||||
|
|
||||||
|
class CursorWrapper(object):
|
||||||
|
"""
|
||||||
|
A thin wrapper around psycopg2's normal cursor class so that we can catch
|
||||||
|
particular exception instances and reraise them with the right types.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, cursor):
|
||||||
|
self.cursor = cursor
|
||||||
|
|
||||||
|
def execute(self, query, args=None):
|
||||||
|
try:
|
||||||
|
return self.cursor.execute(query, args)
|
||||||
|
except Database.IntegrityError, e:
|
||||||
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
except Database.DatabaseError, e:
|
||||||
|
raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
|
||||||
|
def executemany(self, query, args):
|
||||||
|
try:
|
||||||
|
return self.cursor.executemany(query, args)
|
||||||
|
except Database.IntegrityError, e:
|
||||||
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
except Database.DatabaseError, e:
|
||||||
|
raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
if attr in self.__dict__:
|
||||||
|
return self.__dict__[attr]
|
||||||
|
else:
|
||||||
|
return getattr(self.cursor, attr)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.cursor)
|
||||||
|
|
||||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
needs_datetime_string_cast = False
|
needs_datetime_string_cast = False
|
||||||
can_return_id_from_insert = False
|
can_return_id_from_insert = False
|
||||||
|
@ -118,7 +155,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
# versions that support it, but, right now, that's hard to
|
# versions that support it, but, right now, that's hard to
|
||||||
# do without breaking other things (#10509).
|
# do without breaking other things (#10509).
|
||||||
self.features.can_return_id_from_insert = True
|
self.features.can_return_id_from_insert = True
|
||||||
return cursor
|
return CursorWrapper(cursor)
|
||||||
|
|
||||||
def _enter_transaction_management(self, managed):
|
def _enter_transaction_management(self, managed):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -7,6 +7,9 @@ Python 2.5 and later can use a pysqlite2 module or the sqlite3 module in the
|
||||||
standard library.
|
standard library.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from django.db import utils
|
||||||
from django.db.backends import *
|
from django.db.backends import *
|
||||||
from django.db.backends.signals import connection_created
|
from django.db.backends.signals import connection_created
|
||||||
from django.db.backends.sqlite3.client import DatabaseClient
|
from django.db.backends.sqlite3.client import DatabaseClient
|
||||||
|
@ -185,16 +188,25 @@ class SQLiteCursorWrapper(Database.Cursor):
|
||||||
you'll need to use "%%s".
|
you'll need to use "%%s".
|
||||||
"""
|
"""
|
||||||
def execute(self, query, params=()):
|
def execute(self, query, params=()):
|
||||||
query = self.convert_query(query, len(params))
|
try:
|
||||||
return Database.Cursor.execute(self, query, params)
|
query = self.convert_query(query, len(params))
|
||||||
|
return Database.Cursor.execute(self, query, params)
|
||||||
|
except Database.IntegrityError, e:
|
||||||
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
except Database.DatabaseError, e:
|
||||||
|
raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
|
||||||
def executemany(self, query, param_list):
|
def executemany(self, query, param_list):
|
||||||
try:
|
try:
|
||||||
query = self.convert_query(query, len(param_list[0]))
|
query = self.convert_query(query, len(param_list[0]))
|
||||||
return Database.Cursor.executemany(self, query, param_list)
|
return Database.Cursor.executemany(self, query, param_list)
|
||||||
except (IndexError,TypeError):
|
except (IndexError,TypeError):
|
||||||
# No parameter list provided
|
# No parameter list provided
|
||||||
return None
|
return None
|
||||||
|
except Database.IntegrityError, e:
|
||||||
|
raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
except Database.DatabaseError, e:
|
||||||
|
raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
|
||||||
|
|
||||||
def convert_query(self, query, num_params):
|
def convert_query(self, query, num_params):
|
||||||
return query % tuple("?" * num_params)
|
return query % tuple("?" * num_params)
|
||||||
|
|
|
@ -7,6 +7,16 @@ from django.utils.importlib import import_module
|
||||||
|
|
||||||
DEFAULT_DB_ALIAS = 'default'
|
DEFAULT_DB_ALIAS = 'default'
|
||||||
|
|
||||||
|
# Define some exceptions that mirror the PEP249 interface.
|
||||||
|
# We will rethrow any backend-specific errors using these
|
||||||
|
# common wrappers
|
||||||
|
class DatabaseError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class IntegrityError(DatabaseError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def load_backend(backend_name):
|
def load_backend(backend_name):
|
||||||
try:
|
try:
|
||||||
module = import_module('.base', 'django.db.backends.%s' % backend_name)
|
module = import_module('.base', 'django.db.backends.%s' % backend_name)
|
||||||
|
@ -40,9 +50,11 @@ def load_backend(backend_name):
|
||||||
else:
|
else:
|
||||||
raise # If there's some other error, this must be an error in Django itself.
|
raise # If there's some other error, this must be an error in Django itself.
|
||||||
|
|
||||||
|
|
||||||
class ConnectionDoesNotExist(Exception):
|
class ConnectionDoesNotExist(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ConnectionHandler(object):
|
class ConnectionHandler(object):
|
||||||
def __init__(self, databases):
|
def __init__(self, databases):
|
||||||
self.databases = databases
|
self.databases = databases
|
||||||
|
@ -87,6 +99,7 @@ class ConnectionHandler(object):
|
||||||
def all(self):
|
def all(self):
|
||||||
return [self[alias] for alias in self]
|
return [self[alias] for alias in self]
|
||||||
|
|
||||||
|
|
||||||
class ConnectionRouter(object):
|
class ConnectionRouter(object):
|
||||||
def __init__(self, routers):
|
def __init__(self, routers):
|
||||||
self.routers = []
|
self.routers = []
|
||||||
|
|
Loading…
Reference in New Issue