diff options
author | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2007-07-02 07:43:36 +0000 |
---|---|---|
committer | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2007-07-02 07:43:36 +0000 |
commit | 9bf4bacf1346d9f8e3ffff85ece5d52c1d24b902 (patch) | |
tree | 1a782eb78ab2eaab41944ff68af4c5ce2d34b6da | |
parent | 5eb53a6397bcd58563abe1a2bd9bd65b74349b7a (diff) | |
download | django-9bf4bacf1346d9f8e3ffff85ece5d52c1d24b902.tar.gz |
unicode: Added Unicode support for the Oracle backend. All tests pass.
git-svn-id: http://code.djangoproject.com/svn/django/branches/unicode@5584 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r-- | django/db/backends/oracle/base.py | 64 | ||||
-rw-r--r-- | django/db/backends/oracle/creation.py | 10 |
2 files changed, 54 insertions, 20 deletions
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 48b3c27355..f442e3ecdd 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -6,13 +6,18 @@ Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/ from django.conf import settings from django.db.backends import util +from django.utils.datastructures import SortedDict +from django.utils.encoding import smart_str, force_unicode +import datetime +import os + +# Oracle takes client-side character set encoding from the environment. +os.environ['NLS_LANG'] = '.UTF8' try: import cx_Oracle as Database except ImportError, e: from django.core.exceptions import ImproperlyConfigured raise ImproperlyConfigured, "Error loading cx_Oracle module: %s" % e -import datetime -from django.utils.datastructures import SortedDict DatabaseError = Database.Error @@ -45,9 +50,9 @@ class DatabaseWrapper(local): conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME) self.connection = Database.connect(conn_string, **self.options) cursor = FormatStylePlaceholderCursor(self.connection) - # default arraysize of 1 is highly sub-optimal + # Default arraysize of 1 is highly sub-optimal. cursor.arraysize = 100 - # set oracle date to ansi date format + # Set oracle date to ansi date format. cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'") cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'") if settings.DEBUG: @@ -78,23 +83,22 @@ uses_case_insensitive_names = True class FormatStylePlaceholderCursor(Database.Cursor): """ - 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 a query, - you'll need to use "%%s". + 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 + a query, you'll need to use "%%s". + + We also do automatic conversion between Unicode on the Python side and + UTF-8 -- for talking to Oracle -- in here. """ + charset = 'utf-8' + def _rewrite_args(self, query, params=None): if params is None: params = [] else: - # cx_Oracle can't handle unicode parameters, so cast to str for now - for i, param in enumerate(params): - if type(param) == unicode: - try: - params[i] = param.encode('utf-8') - except UnicodeError: - params[i] = str(param) + params = self._format_params(params) args = [(':arg%d' % i) for i in range(len(params))] - query = query % tuple(args) + query = smart_str(query, self.charset) % tuple(args) # cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it # it does want a trailing ';' but not a trailing '/'. However, these # characters must be included in the original query in case the query @@ -103,6 +107,16 @@ class FormatStylePlaceholderCursor(Database.Cursor): query = query[:-1] return query, params + def _format_params(self, params): + if isinstance(params, dict): + result = {} + charset = self.charset + for key, value in params.items(): + result[smart_str(key, charset)] = smart_str(value, charset) + return result + else: + return tuple([smart_str(p, self.charset, True) for p in params]) + def execute(self, query, params=None): query, params = self._rewrite_args(query, params) return Database.Cursor.execute(self, query, params) @@ -111,6 +125,26 @@ class FormatStylePlaceholderCursor(Database.Cursor): query, params = self._rewrite_args(query, params) return Database.Cursor.executemany(self, query, params) + def fetchone(self): + return to_unicode(Database.Cursor.fetchone(self)) + + def fetchmany(self, size=None): + if size is None: + size = self.arraysize + return tuple([tuple([to_unicode(e) for e in r]) for r in Database.Cursor.fetchmany(self, size)]) + + def fetchall(self): + return tuple([tuple([to_unicode(e) for e in r]) for r in Database.Cursor.fetchall(self)]) + +def to_unicode(s): + """ + Convert strings to Unicode objects (and return all other data types + unchanged). + """ + if isinstance(s, basestring): + return force_unicode(s) + return s + def quote_name(name): # SQL92 requires delimited (quoted) names to be case-sensitive. When # not quoted, Oracle has case-insensitive behavior for identifiers, but diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py index 6b1a30d55d..f7903bce4f 100644 --- a/django/db/backends/oracle/creation.py +++ b/django/db/backends/oracle/creation.py @@ -8,15 +8,15 @@ from django.core import management DATA_TYPES = { 'AutoField': 'NUMBER(11)', 'BooleanField': 'NUMBER(1) CHECK (%(column)s IN (0,1))', - 'CharField': 'VARCHAR2(%(maxlength)s)', + 'CharField': 'NVARCHAR2(%(maxlength)s)', 'CommaSeparatedIntegerField': 'VARCHAR2(%(maxlength)s)', 'DateField': 'DATE', 'DateTimeField': 'TIMESTAMP', 'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)', - 'FileField': 'VARCHAR2(100)', - 'FilePathField': 'VARCHAR2(100)', + 'FileField': 'NVARCHAR2(100)', + 'FilePathField': 'NVARCHAR2(100)', 'FloatField': 'DOUBLE PRECISION', - 'ImageField': 'VARCHAR2(100)', + 'ImageField': 'NVARCHAR2(100)', 'IntegerField': 'NUMBER(11)', 'IPAddressField': 'VARCHAR2(15)', 'ManyToManyField': None, @@ -25,7 +25,7 @@ DATA_TYPES = { 'PhoneNumberField': 'VARCHAR2(20)', 'PositiveIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)', 'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)', - 'SlugField': 'VARCHAR2(50)', + 'SlugField': 'NVARCHAR2(50)', 'SmallIntegerField': 'NUMBER(11)', 'TextField': 'NCLOB', 'TimeField': 'TIMESTAMP', |