Fixed #5543: callproc() and friends now work with Oracle and our FormatStylePlaceholderCursor.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@9767 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
6332ad4804
commit
b41a45f8e5
|
@ -291,10 +291,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
pass
|
pass
|
||||||
if not cursor:
|
if not cursor:
|
||||||
cursor = FormatStylePlaceholderCursor(self.connection)
|
cursor = FormatStylePlaceholderCursor(self.connection)
|
||||||
# Necessary to retrieve decimal values without rounding error.
|
|
||||||
cursor.numbersAsStrings = True
|
|
||||||
# Default arraysize of 1 is highly sub-optimal.
|
|
||||||
cursor.arraysize = 100
|
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
|
|
||||||
|
@ -320,7 +316,7 @@ class OracleParam(object):
|
||||||
self.input_size = None
|
self.input_size = None
|
||||||
|
|
||||||
|
|
||||||
class FormatStylePlaceholderCursor(Database.Cursor):
|
class FormatStylePlaceholderCursor(object):
|
||||||
"""
|
"""
|
||||||
Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var"
|
Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var"
|
||||||
style. This fixes it -- but note that if you want to use a literal "%s" in
|
style. This fixes it -- but note that if you want to use a literal "%s" in
|
||||||
|
@ -331,6 +327,13 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
"""
|
"""
|
||||||
charset = 'utf-8'
|
charset = 'utf-8'
|
||||||
|
|
||||||
|
def __init__(self, connection):
|
||||||
|
self.cursor = connection.cursor()
|
||||||
|
# Necessary to retrieve decimal values without rounding error.
|
||||||
|
self.cursor.numbersAsStrings = True
|
||||||
|
# Default arraysize of 1 is highly sub-optimal.
|
||||||
|
self.cursor.arraysize = 100
|
||||||
|
|
||||||
def _format_params(self, params):
|
def _format_params(self, params):
|
||||||
return tuple([OracleParam(p, self.charset, True) for p in params])
|
return tuple([OracleParam(p, self.charset, True) for p in params])
|
||||||
|
|
||||||
|
@ -360,8 +363,7 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
query = smart_str(query, self.charset) % tuple(args)
|
query = smart_str(query, self.charset) % tuple(args)
|
||||||
self._guess_input_sizes([params])
|
self._guess_input_sizes([params])
|
||||||
try:
|
try:
|
||||||
return Database.Cursor.execute(self, query,
|
return self.cursor.execute(query, self._param_generator(params))
|
||||||
self._param_generator(params))
|
|
||||||
except DatabaseError, e:
|
except 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):
|
||||||
|
@ -384,7 +386,7 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
formatted = [self._format_params(i) for i in params]
|
formatted = [self._format_params(i) for i in params]
|
||||||
self._guess_input_sizes(formatted)
|
self._guess_input_sizes(formatted)
|
||||||
try:
|
try:
|
||||||
return Database.Cursor.executemany(self, 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 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.
|
||||||
|
@ -393,7 +395,7 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def fetchone(self):
|
def fetchone(self):
|
||||||
row = Database.Cursor.fetchone(self)
|
row = self.cursor.fetchone()
|
||||||
if row is None:
|
if row is None:
|
||||||
return row
|
return row
|
||||||
return self._rowfactory(row)
|
return self._rowfactory(row)
|
||||||
|
@ -402,17 +404,17 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
if size is None:
|
if size is None:
|
||||||
size = self.arraysize
|
size = self.arraysize
|
||||||
return tuple([self._rowfactory(r)
|
return tuple([self._rowfactory(r)
|
||||||
for r in Database.Cursor.fetchmany(self, size)])
|
for r in self.cursor.fetchmany(size)])
|
||||||
|
|
||||||
def fetchall(self):
|
def fetchall(self):
|
||||||
return tuple([self._rowfactory(r)
|
return tuple([self._rowfactory(r)
|
||||||
for r in Database.Cursor.fetchall(self)])
|
for r in self.cursor.fetchall()])
|
||||||
|
|
||||||
def _rowfactory(self, row):
|
def _rowfactory(self, row):
|
||||||
# Cast numeric values as the appropriate Python type based upon the
|
# Cast numeric values as the appropriate Python type based upon the
|
||||||
# cursor description, and convert strings to unicode.
|
# cursor description, and convert strings to unicode.
|
||||||
casted = []
|
casted = []
|
||||||
for value, desc in zip(row, self.description):
|
for value, desc in zip(row, self.cursor.description):
|
||||||
if value is not None and desc[1] is Database.NUMBER:
|
if value is not None and desc[1] is Database.NUMBER:
|
||||||
precision, scale = desc[4:6]
|
precision, scale = desc[4:6]
|
||||||
if scale == -127:
|
if scale == -127:
|
||||||
|
@ -447,6 +449,15 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
casted.append(value)
|
casted.append(value)
|
||||||
return tuple(casted)
|
return tuple(casted)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
def to_unicode(s):
|
def to_unicode(s):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Unit tests for specific database backends.
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from django.db import connection
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
class Callproc(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_dbms_session(self):
|
||||||
|
# If the backend is Oracle, test that we can call a standard
|
||||||
|
# stored procedure through our cursor wrapper.
|
||||||
|
if settings.DATABASE_ENGINE == 'oracle':
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.callproc('DBMS_SESSION.SET_IDENTIFIER',
|
||||||
|
['_django_testing!',])
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue