From b7a67b788cf2fa59a84f933492413379eb3cab83 Mon Sep 17 00:00:00 2001
From: Shai Berger <shai@platonix.com>
Date: Fri, 16 May 2014 20:04:26 +0300
Subject: [PATCH] [1.7.x] Fixed storing of binary fields and unicode textfields
 for Oracle/Python3

Backport of 6bb6df2943 from master
---
 django/db/backends/oracle/base.py | 10 ++++++++--
 tests/model_regress/tests.py      | 11 +++++++++++
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index f945b26101a..0184fa1cbee 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -748,6 +748,7 @@ class OracleParam(object):
                 param = timezone.make_aware(param, default_timezone)
             param = Oracle_datetime.from_datetime(param.astimezone(timezone.utc))
 
+        string_size = 0
         # Oracle doesn't recognize True and False correctly in Python 3.
         # The conversion done below works both in 2 and 3.
         if param is True:
@@ -756,15 +757,20 @@ class OracleParam(object):
             param = "0"
         if hasattr(param, 'bind_parameter'):
             self.force_bytes = param.bind_parameter(cursor)
-        elif isinstance(param, six.memoryview):
+        elif isinstance(param, Database.Binary):
             self.force_bytes = param
         else:
+            # To transmit to the database, we need Unicode if supported
+            # To get size right, we must consider bytes.
             self.force_bytes = convert_unicode(param, cursor.charset,
                                              strings_only)
+            if isinstance(self.force_bytes, six.string_types):
+                # We could optimize by only converting up to 4000 bytes here
+                string_size = len(force_bytes(param, cursor.charset, strings_only))
         if hasattr(param, 'input_size'):
             # If parameter has `input_size` attribute, use that.
             self.input_size = param.input_size
-        elif isinstance(param, six.string_types) and len(param) > 4000:
+        elif string_size > 4000:
             # Mark any string param greater than 4000 characters as a CLOB.
             self.input_size = Database.CLOB
         else:
diff --git a/tests/model_regress/tests.py b/tests/model_regress/tests.py
index 952ba45aab4..057798c785f 100644
--- a/tests/model_regress/tests.py
+++ b/tests/model_regress/tests.py
@@ -66,6 +66,17 @@ class ModelTests(TestCase):
         a = Article.objects.get(pk=a.pk)
         self.assertEqual(len(a.article_text), 5000)
 
+    def test_long_unicode_textfield(self):
+        # TextFields can hold more than 4000 bytes also when they are
+        # less than 4000 characters
+        a = Article.objects.create(
+            headline="Really, really big",
+            pub_date=datetime.datetime.now(),
+            article_text='\u05d0\u05d1\u05d2' * 1000
+        )
+        a = Article.objects.get(pk=a.pk)
+        self.assertEqual(len(a.article_text), 3000)
+
     def test_date_lookup(self):
         # Regression test for #659
         Party.objects.create(when=datetime.datetime(1999, 12, 31))