Fixed #27649 -- Bumped required cx_Oracle to 5.2.

Removed obsolete workarounds from 1aa4889808
and dcf3be7a62.
This commit is contained in:
Tim Graham 2016-12-29 12:45:25 -05:00 committed by GitHub
parent 5eff8a7783
commit fae56427e1
6 changed files with 19 additions and 60 deletions

View File

@ -62,7 +62,7 @@ from .features import DatabaseFeatures # NOQA isort:skip
from .introspection import DatabaseIntrospection # NOQA isort:skip from .introspection import DatabaseIntrospection # NOQA isort:skip
from .operations import DatabaseOperations # NOQA isort:skip from .operations import DatabaseOperations # NOQA isort:skip
from .schema import DatabaseSchemaEditor # NOQA isort:skip from .schema import DatabaseSchemaEditor # NOQA isort:skip
from .utils import Oracle_datetime, convert_unicode # NOQA isort:skip from .utils import Oracle_datetime # NOQA isort:skip
class _UninitializedOperatorsDescriptor(object): class _UninitializedOperatorsDescriptor(object):
@ -208,8 +208,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
return conn_params return conn_params
def get_new_connection(self, conn_params): def get_new_connection(self, conn_params):
conn_string = convert_unicode(self._connect_string()) return Database.connect(self._connect_string(), **conn_params)
return Database.connect(conn_string, **conn_params)
def init_connection_state(self): def init_connection_state(self):
cursor = self.create_cursor() cursor = self.create_cursor()
@ -246,13 +245,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
self.operators = self._standard_operators self.operators = self._standard_operators
self.pattern_ops = self._standard_pattern_ops self.pattern_ops = self._standard_pattern_ops
cursor.close() cursor.close()
try:
self.connection.stmtcachesize = 20 self.connection.stmtcachesize = 20
except AttributeError:
# Django docs specify cx_Oracle version 4.3.1 or higher, but
# stmtcachesize is available only in 4.3.2 and up.
pass
# Ensure all changes are preserved even when AUTOCOMMIT is False. # Ensure all changes are preserved even when AUTOCOMMIT is False.
if not self.get_autocommit(): if not self.get_autocommit():
self.commit() self.commit()
@ -265,7 +258,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
try: try:
return self.connection.commit() return self.connection.commit()
except Database.DatabaseError as e: except Database.DatabaseError as e:
# cx_Oracle 5.0.4 raises a cx_Oracle.DatabaseError exception # cx_Oracle raises a cx_Oracle.DatabaseError exception
# with the following attributes and values: # with the following attributes and values:
# code = 2091 # code = 2091
# message = 'ORA-02091: transaction rolled back # message = 'ORA-02091: transaction rolled back
@ -363,7 +356,7 @@ class OracleParam(object):
else: else:
# To transmit to the database, we need Unicode if supported # To transmit to the database, we need Unicode if supported
# To get size right, we must consider bytes. # To get size right, we must consider bytes.
self.force_bytes = convert_unicode(param, cursor.charset, strings_only) self.force_bytes = force_text(param, cursor.charset, strings_only)
if isinstance(self.force_bytes, six.string_types): if isinstance(self.force_bytes, six.string_types):
# We could optimize by only converting up to 4000 bytes here # We could optimize by only converting up to 4000 bytes here
string_size = len(force_bytes(param, cursor.charset, strings_only)) string_size = len(force_bytes(param, cursor.charset, strings_only))
@ -459,11 +452,11 @@ class FormatStylePlaceholderCursor(object):
query = query[:-1] query = query[:-1]
if params is None: if params is None:
params = [] params = []
query = convert_unicode(query, self.charset) query = query
elif hasattr(params, 'keys'): elif hasattr(params, 'keys'):
# Handle params as dict # Handle params as dict
args = {k: ":%s" % k for k in params.keys()} args = {k: ":%s" % k for k in params.keys()}
query = convert_unicode(query % args, self.charset) query = query % args
elif unify_by_values and len(params) > 0: elif unify_by_values and len(params) > 0:
# Handle params as a dict with unified query parameters by their # Handle params as a dict with unified query parameters by their
# values. It can be used only in single query execute() because # values. It can be used only in single query execute() because
@ -480,23 +473,17 @@ class FormatStylePlaceholderCursor(object):
params_dict = {param: ':arg%d' % i for i, param in enumerate(set(params))} params_dict = {param: ':arg%d' % i for i, param in enumerate(set(params))}
args = [params_dict[param] for param in params] args = [params_dict[param] for param in params]
params = dict(zip(params_dict.values(), list(zip(*params_dict.keys()))[0])) params = dict(zip(params_dict.values(), list(zip(*params_dict.keys()))[0]))
query = convert_unicode(query % tuple(args), self.charset) query = query % tuple(args)
else: else:
# Handle params as sequence # Handle params as sequence
args = [(':arg%d' % i) for i in range(len(params))] args = [(':arg%d' % i) for i in range(len(params))]
query = convert_unicode(query % tuple(args), self.charset) query = query % tuple(args)
return query, self._format_params(params) return force_text(query, self.charset), self._format_params(params)
def execute(self, query, params=None): def execute(self, query, params=None):
query, params = self._fix_for_params(query, params, unify_by_values=True) query, params = self._fix_for_params(query, params, unify_by_values=True)
self._guess_input_sizes([params]) self._guess_input_sizes([params])
try:
return self.cursor.execute(query, self._param_generator(params)) return self.cursor.execute(query, self._param_generator(params))
except Database.DatabaseError as e:
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
if hasattr(e.args[0], 'code') and e.args[0].code == 1400 and not isinstance(e, Database.IntegrityError):
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
raise
def executemany(self, query, params=None): def executemany(self, query, params=None):
if not params: if not params:
@ -509,13 +496,7 @@ class FormatStylePlaceholderCursor(object):
# more than once, we can't make it lazy by using a generator # more than once, we can't make it lazy by using a generator
formatted = [firstparams] + [self._format_params(p) for p in params_iter] formatted = [firstparams] + [self._format_params(p) for p in params_iter]
self._guess_input_sizes(formatted) self._guess_input_sizes(formatted)
try:
return self.cursor.executemany(query, [self._param_generator(p) for p in formatted]) return self.cursor.executemany(query, [self._param_generator(p) for p in formatted])
except Database.DatabaseError as e:
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
if hasattr(e.args[0], 'code') and e.args[0].code == 1400 and not isinstance(e, Database.IntegrityError):
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
raise
def fetchone(self): def fetchone(self):
row = self.cursor.fetchone() row = self.cursor.fetchone()

View File

@ -11,7 +11,7 @@ from django.utils import six, timezone
from django.utils.encoding import force_bytes, force_text from django.utils.encoding import force_bytes, force_text
from .base import Database from .base import Database
from .utils import InsertIdVar, Oracle_datetime, convert_unicode from .utils import InsertIdVar, Oracle_datetime
class DatabaseOperations(BaseDatabaseOperations): class DatabaseOperations(BaseDatabaseOperations):
@ -310,10 +310,10 @@ WHEN (new.%(col_name)s IS NULL)
return "RETURNING %s INTO %%s", (InsertIdVar(),) return "RETURNING %s INTO %%s", (InsertIdVar(),)
def savepoint_create_sql(self, sid): def savepoint_create_sql(self, sid):
return convert_unicode("SAVEPOINT " + self.quote_name(sid)) return "SAVEPOINT " + self.quote_name(sid)
def savepoint_rollback_sql(self, sid): def savepoint_rollback_sql(self, sid):
return convert_unicode("ROLLBACK TO SAVEPOINT " + self.quote_name(sid)) return "ROLLBACK TO SAVEPOINT " + self.quote_name(sid)
def sql_flush(self, style, tables, sequences, allow_cascade=False): def sql_flush(self, style, tables, sequences, allow_cascade=False):
# Return a list of 'TRUNCATE x;', 'TRUNCATE y;', # Return a list of 'TRUNCATE x;', 'TRUNCATE y;',

View File

@ -1,18 +1,7 @@
import datetime import datetime
from django.utils.encoding import force_bytes, force_text
from .base import Database from .base import Database
# Check whether cx_Oracle was compiled with the WITH_UNICODE option if cx_Oracle is pre-5.1. This will
# also be True for cx_Oracle 5.1 and in Python 3.0. See #19606
if int(Database.version.split('.', 1)[0]) >= 5 and \
(int(Database.version.split('.', 2)[1]) >= 1 or
not hasattr(Database, 'UNICODE')):
convert_unicode = force_text
else:
convert_unicode = force_bytes
class InsertIdVar(object): class InsertIdVar(object):
""" """

View File

@ -731,17 +731,7 @@ Oracle notes
============ ============
Django supports `Oracle Database Server`_ versions 11.2 and higher. Version Django supports `Oracle Database Server`_ versions 11.2 and higher. Version
4.3.1 or higher of the `cx_Oracle`_ Python driver is required, although we 5.2 or higher of the `cx_Oracle`_ Python driver is required.
recommend version 5.1.3 or later as these versions support Python 3.
Note that due to a Unicode-corruption bug in ``cx_Oracle`` 5.0, that
version of the driver should **not** be used with Django;
``cx_Oracle`` 5.0.1 resolved this issue, so if you'd like to use a
more recent ``cx_Oracle``, use version 5.0.1.
``cx_Oracle`` 5.0.1 or greater can optionally be compiled with the
``WITH_UNICODE`` environment variable. This is recommended but not
required.
.. _`Oracle Database Server`: http://www.oracle.com/ .. _`Oracle Database Server`: http://www.oracle.com/
.. _`cx_Oracle`: http://cx-oracle.sourceforge.net/ .. _`cx_Oracle`: http://cx-oracle.sourceforge.net/

View File

@ -698,6 +698,8 @@ Miscellaneous
leaves ``request.POST`` immutable. If you're modifying that ``QueryDict``, leaves ``request.POST`` immutable. If you're modifying that ``QueryDict``,
you must now first copy it, e.g. ``request.POST.copy()``. you must now first copy it, e.g. ``request.POST.copy()``.
* Support for ``cx_Oracle`` < 5.2 is removed.
.. _deprecated-features-1.11: .. _deprecated-features-1.11:
Features deprecated in 1.11 Features deprecated in 1.11

View File

@ -86,11 +86,8 @@ class OracleTests(unittest.TestCase):
def test_dbms_session(self): def test_dbms_session(self):
# If the backend is Oracle, test that we can call a standard # If the backend is Oracle, test that we can call a standard
# stored procedure through our cursor wrapper. # stored procedure through our cursor wrapper.
from django.db.backends.oracle.base import convert_unicode
with connection.cursor() as cursor: with connection.cursor() as cursor:
cursor.callproc(convert_unicode('DBMS_SESSION.SET_IDENTIFIER'), cursor.callproc('DBMS_SESSION.SET_IDENTIFIER', ['_django_testing!'])
[convert_unicode('_django_testing!')])
def test_cursor_var(self): def test_cursor_var(self):
# If the backend is Oracle, test that we can pass cursor variables # If the backend is Oracle, test that we can pass cursor variables