summaryrefslogtreecommitdiff
path: root/django/db
diff options
context:
space:
mode:
Diffstat (limited to 'django/db')
-rw-r--r--django/db/backends/__init__.py5
-rw-r--r--django/db/backends/oracle/base.py66
-rw-r--r--django/db/backends/sqlite3/base.py1
-rw-r--r--django/db/models/query.py5
4 files changed, 52 insertions, 25 deletions
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index 1a74232704..9abb9a9637 100644
--- a/django/db/backends/__init__.py
+++ b/django/db/backends/__init__.py
@@ -613,6 +613,11 @@ class BaseDatabaseFeatures(object):
# when autocommit is disabled? http://bugs.python.org/issue8145#msg109965
autocommits_when_autocommit_is_off = False
+ # Does the backend support 'pyformat' style ("... %(name)s ...", {'name': value})
+ # parameter passing? Note this can be provided by the backend even if not
+ # supported by the Python driver
+ supports_paramstyle_pyformat = True
+
def __init__(self, connection):
self.connection = connection
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index 3f39a15aa7..5e2b763f52 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -757,20 +757,37 @@ class FormatStylePlaceholderCursor(object):
self.cursor.arraysize = 100
def _format_params(self, params):
- return tuple([OracleParam(p, self, True) for p in params])
+ try:
+ return dict((k,OracleParam(v, self, True)) for k,v in params.items())
+ except AttributeError:
+ return tuple([OracleParam(p, self, True) for p in params])
def _guess_input_sizes(self, params_list):
- sizes = [None] * len(params_list[0])
- for params in params_list:
- for i, value in enumerate(params):
- if value.input_size:
- sizes[i] = value.input_size
- self.setinputsizes(*sizes)
+ # Try dict handling; if that fails, treat as sequence
+ if hasattr(params_list[0], 'keys'):
+ sizes = {}
+ for params in params_list:
+ for k, value in params.items():
+ if value.input_size:
+ sizes[k] = value.input_size
+ self.setinputsizes(**sizes)
+ else:
+ # It's not a list of dicts; it's a list of sequences
+ sizes = [None] * len(params_list[0])
+ for params in params_list:
+ for i, value in enumerate(params):
+ if value.input_size:
+ sizes[i] = value.input_size
+ self.setinputsizes(*sizes)
def _param_generator(self, params):
- return [p.force_bytes for p in params]
+ # Try dict handling; if that fails, treat as sequence
+ if hasattr(params, 'items'):
+ return dict((k, v.force_bytes) for k,v in params.items())
+ else:
+ return [p.force_bytes for p in params]
- def execute(self, query, params=None):
+ def _fix_for_params(self, query, params):
# 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
@@ -780,10 +797,18 @@ class FormatStylePlaceholderCursor(object):
if params is None:
params = []
query = convert_unicode(query, self.charset)
+ elif hasattr(params, 'keys'):
+ # Handle params as dict
+ args = dict((k, ":%s"%k) for k in params.keys())
+ query = convert_unicode(query % args, self.charset)
else:
- params = self._format_params(params)
+ # Handle params as sequence
args = [(':arg%d' % i) for i in range(len(params))]
query = convert_unicode(query % tuple(args), self.charset)
+ return query, self._format_params(params)
+
+ def execute(self, query, params=None):
+ query, params = self._fix_for_params(query, params)
self._guess_input_sizes([params])
try:
return self.cursor.execute(query, self._param_generator(params))
@@ -794,22 +819,15 @@ class FormatStylePlaceholderCursor(object):
raise
def executemany(self, query, params=None):
- # cx_Oracle doesn't support iterators, convert them to lists
- if params is not None and not isinstance(params, (list, tuple)):
- params = list(params)
- try:
- args = [(':arg%d' % i) for i in range(len(params[0]))]
- except (IndexError, TypeError):
+ if not params:
# No params given, nothing to do
return None
- # 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
- # is being passed to SQL*Plus.
- if query.endswith(';') or query.endswith('/'):
- query = query[:-1]
- query = convert_unicode(query % tuple(args), self.charset)
- formatted = [self._format_params(i) for i in params]
+ # uniform treatment for sequences and iterables
+ params_iter = iter(params)
+ query, firstparams = self._fix_for_params(query, next(params_iter))
+ # we build a list of formatted params; as we're going to traverse it
+ # more than once, we can't make it lazy by using a generator
+ formatted = [firstparams]+[self._format_params(p) for p in params_iter]
self._guess_input_sizes(formatted)
try:
return self.cursor.executemany(query,
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index 324adfd97b..92dbf354ae 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -101,6 +101,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
has_bulk_insert = True
can_combine_inserts_with_and_without_auto_increment_pk = False
autocommits_when_autocommit_is_off = True
+ supports_paramstyle_pyformat = False
@cached_property
def uses_savepoints(self):
diff --git a/django/db/models/query.py b/django/db/models/query.py
index b0ce25f5b5..27a87a3f65 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -1445,7 +1445,10 @@ class RawQuerySet(object):
yield instance
def __repr__(self):
- return "<RawQuerySet: %r>" % (self.raw_query % tuple(self.params))
+ text = self.raw_query
+ if self.params:
+ text = text % (self.params if hasattr(self.params, 'keys') else tuple(self.params))
+ return "<RawQuerySet: %r>" % text
def __getitem__(self, k):
return list(self)[k]