From f0e35e1bbe9c5e35717482c5c5df11abfc70fd76 Mon Sep 17 00:00:00 2001
From: Matt Boersma <matt@sprout.org>
Date: Fri, 16 Jan 2009 22:24:45 +0000
Subject: [PATCH] [1.0.X] Fixed #5543: callproc() and friends now work with
 Oracle and our FormatStylePlaceholderCursor.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@9768 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()