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