From b41a45f8e5fa87673d984663d39531292fd8a100 Mon Sep 17 00:00:00 2001 From: Matt Boersma Date: Fri, 16 Jan 2009 22:23:58 +0000 Subject: [PATCH] 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 --- django/db/backends/oracle/base.py | 35 ++++++++++++++++--------- tests/regressiontests/backends/tests.py | 26 ++++++++++++++++++ 2 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 tests/regressiontests/backends/tests.py diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 6d8816a9d1..a73f111314 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -291,10 +291,6 @@ class DatabaseWrapper(BaseDatabaseWrapper): pass if not cursor: 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 @@ -320,7 +316,7 @@ class OracleParam(object): self.input_size = None -class FormatStylePlaceholderCursor(Database.Cursor): +class FormatStylePlaceholderCursor(object): """ 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 @@ -331,6 +327,13 @@ class FormatStylePlaceholderCursor(Database.Cursor): """ 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): 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) self._guess_input_sizes([params]) try: - return Database.Cursor.execute(self, query, - self._param_generator(params)) + return self.cursor.execute(query, self._param_generator(params)) except DatabaseError, e: # cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400. 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] self._guess_input_sizes(formatted) try: - return Database.Cursor.executemany(self, query, + return self.cursor.executemany(query, [self._param_generator(p) for p in formatted]) except DatabaseError, e: # cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400. @@ -393,7 +395,7 @@ class FormatStylePlaceholderCursor(Database.Cursor): raise e def fetchone(self): - row = Database.Cursor.fetchone(self) + row = self.cursor.fetchone() if row is None: return row return self._rowfactory(row) @@ -402,17 +404,17 @@ class FormatStylePlaceholderCursor(Database.Cursor): if size is None: size = self.arraysize return tuple([self._rowfactory(r) - for r in Database.Cursor.fetchmany(self, size)]) + for r in self.cursor.fetchmany(size)]) def fetchall(self): return tuple([self._rowfactory(r) - for r in Database.Cursor.fetchall(self)]) + for r in self.cursor.fetchall()]) def _rowfactory(self, row): # Cast numeric values as the appropriate Python type based upon the # cursor description, and convert strings to unicode. 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: precision, scale = desc[4:6] if scale == -127: @@ -447,6 +449,15 @@ class FormatStylePlaceholderCursor(Database.Cursor): casted.append(value) 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): """ diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py new file mode 100644 index 0000000000..5dec9feee6 --- /dev/null +++ b/tests/regressiontests/backends/tests.py @@ -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()