summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMalcolm Tredinnick <malcolm.tredinnick@gmail.com>2008-04-25 06:54:35 +0000
committerMalcolm Tredinnick <malcolm.tredinnick@gmail.com>2008-04-25 06:54:35 +0000
commit6657d476d03fbc5cd1d9a64922f2a8b7c97ddf05 (patch)
tree48e49b06232470deccb90718b77143695d91c6e7
parent8a4e1de8b0573ce4330f7690fe5e9d786265e237 (diff)
queryset-refactor: An attempt to fix an ambiguous column error that can arise
in Oracle queries. Adds an options to add output column aliases. The testing I can do shows that the query is being constructed correctly. I haven't run the test against an Oracle install, though. Refs #7057 (ticket can be closed if the Oracle tests pass). git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7457 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/db/backends/oracle/query.py8
-rw-r--r--django/db/models/sql/query.py49
2 files changed, 44 insertions, 13 deletions
diff --git a/django/db/backends/oracle/query.py b/django/db/backends/oracle/query.py
index 49f695ed94..a883a0b308 100644
--- a/django/db/backends/oracle/query.py
+++ b/django/db/backends/oracle/query.py
@@ -72,7 +72,7 @@ def query_class(QueryClass, Database):
values.append(value)
return values
- def as_sql(self, with_limits=True):
+ def as_sql(self, with_limits=True, with_col_aliases=False):
"""
Creates the SQL for this query. Returns the SQL string and list
of parameters. This is overriden from the original Query class
@@ -88,7 +88,8 @@ def query_class(QueryClass, Database):
# If no offsets, just return the result of the base class
# `as_sql`.
if not do_offset:
- return super(OracleQuery, self).as_sql(with_limits=False)
+ return super(OracleQuery, self).as_sql(with_limits=False,
+ with_col_aliases=with_col_aliases)
# `get_columns` needs to be called before `get_ordering` to
# populate `_select_alias`.
@@ -110,7 +111,8 @@ def query_class(QueryClass, Database):
# Getting the selection SQL and the params, which has the `rn`
# extra selection SQL.
self.extra_select['rn'] = 'ROW_NUMBER() OVER (ORDER BY %s )' % rn_orderby
- sql, params= super(OracleQuery, self).as_sql(with_limits=False)
+ sql, params= super(OracleQuery, self).as_sql(with_limits=False,
+ with_col_aliases=True)
# Constructing the result SQL, using the initial select SQL
# obtained above.
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 2dba2bdb3c..502769b932 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -206,7 +206,7 @@ class Query(object):
return number
- def as_sql(self, with_limits=True):
+ def as_sql(self, with_limits=True, with_col_aliases=False):
"""
Creates the SQL for this query. Returns the SQL string and list of
parameters.
@@ -215,7 +215,7 @@ class Query(object):
in the query.
"""
self.pre_sql_setup()
- out_cols = self.get_columns()
+ out_cols = self.get_columns(with_col_aliases)
ordering = self.get_ordering()
# This must come after 'select' and 'ordering' -- see docstring of
@@ -354,38 +354,62 @@ class Query(object):
if self.select_related and not self.related_select_cols:
self.fill_related_selections()
- def get_columns(self):
+ def get_columns(self, with_aliases=False):
"""
Return the list of columns to use in the select statement. If no
columns have been specified, returns all columns relating to fields in
the model.
+
+ If 'with_aliases' is true, any column names that are duplicated
+ (without the table names) are given unique aliases. This is needed in
+ some cases to avoid ambiguitity with nested queries.
"""
qn = self.quote_name_unless_alias
result = ['(%s) AS %s' % (col, alias) for alias, col in self.extra_select.iteritems()]
aliases = self.extra_select.keys()
+ if with_aliases:
+ col_aliases = set(aliases)
+ else:
+ col_aliases = set()
if self.select:
for col in self.select:
if isinstance(col, (list, tuple)):
r = '%s.%s' % (qn(col[0]), qn(col[1]))
- result.append(r)
- aliases.append(r)
+ if with_aliases and col[1] in col_aliases:
+ c_alias = 'Col%d' % len(col_aliases)
+ result.append('%s AS %s' % (r, c_alias))
+ aliases.append(c_alias)
+ col_aliases.add(c_alias)
+ else:
+ result.append(r)
+ aliases.append(r)
+ col_aliases.add(col[1])
else:
result.append(col.as_sql(quote_func=qn))
if hasattr(col, 'alias'):
aliases.append(col.alias)
+ col_aliases.add(col.alias)
elif self.default_cols:
- cols = self.get_default_columns(True)
+ cols = self.get_default_columns(True, with_aliases, col_aliases)
result.extend(cols)
aliases.extend(cols)
for table, col in self.related_select_cols:
r = '%s.%s' % (qn(table), qn(col))
- result.append(r)
- aliases.append(r)
+ if with_aliases and col in col_aliases:
+ c_alias = 'Col%d' % len(col_aliases)
+ result.append('%s AS %s' % (r, c_alias))
+ aliases.append(c_alias)
+ col_aliases.add(c_alias)
+ else:
+ result.append(r)
+ aliases.append(r)
+ col_aliases.add(col)
self._select_aliases = set(aliases)
return result
- def get_default_columns(self, as_str=False):
+ def get_default_columns(self, as_str=False, with_aliases=False,
+ col_aliases=None):
"""
Computes the default columns for selecting every field in the base
model. Returns a list of default (alias, column) pairs suitable for
@@ -406,7 +430,12 @@ class Query(object):
root_pk, model._meta.pk.column))
seen[model] = alias
if as_str:
- result.append('%s.%s' % (qn(alias), qn2(field.column)))
+ if with_aliases and field.column in col_aliases:
+ c_alias = 'Col%d' % len(col_aliases)
+ result.append('%s.%s AS %s' % (qn(alias),
+ qn2(field.column), c_aliase))
+ else:
+ result.append('%s.%s' % (qn(alias), qn2(field.column)))
else:
result.append((alias, field.column))
return result