1
0
mirror of https://github.com/django/django.git synced 2025-07-05 10:19:20 +00:00

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
This commit is contained in:
Malcolm Tredinnick 2008-04-25 06:54:35 +00:00
parent 8a4e1de8b0
commit 6657d476d0
2 changed files with 44 additions and 13 deletions

View File

@ -72,7 +72,7 @@ def query_class(QueryClass, Database):
values.append(value) values.append(value)
return values 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 Creates the SQL for this query. Returns the SQL string and list
of parameters. This is overriden from the original Query class 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 # If no offsets, just return the result of the base class
# `as_sql`. # `as_sql`.
if not do_offset: 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 # `get_columns` needs to be called before `get_ordering` to
# populate `_select_alias`. # populate `_select_alias`.
@ -110,7 +111,8 @@ def query_class(QueryClass, Database):
# Getting the selection SQL and the params, which has the `rn` # Getting the selection SQL and the params, which has the `rn`
# extra selection SQL. # extra selection SQL.
self.extra_select['rn'] = 'ROW_NUMBER() OVER (ORDER BY %s )' % rn_orderby 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 # Constructing the result SQL, using the initial select SQL
# obtained above. # obtained above.

View File

@ -206,7 +206,7 @@ class Query(object):
return number 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 Creates the SQL for this query. Returns the SQL string and list of
parameters. parameters.
@ -215,7 +215,7 @@ class Query(object):
in the query. in the query.
""" """
self.pre_sql_setup() self.pre_sql_setup()
out_cols = self.get_columns() out_cols = self.get_columns(with_col_aliases)
ordering = self.get_ordering() ordering = self.get_ordering()
# This must come after 'select' and 'ordering' -- see docstring of # 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: if self.select_related and not self.related_select_cols:
self.fill_related_selections() 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 Return the list of columns to use in the select statement. If no
columns have been specified, returns all columns relating to fields in columns have been specified, returns all columns relating to fields in
the model. 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 qn = self.quote_name_unless_alias
result = ['(%s) AS %s' % (col, alias) for alias, col in self.extra_select.iteritems()] result = ['(%s) AS %s' % (col, alias) for alias, col in self.extra_select.iteritems()]
aliases = self.extra_select.keys() aliases = self.extra_select.keys()
if with_aliases:
col_aliases = set(aliases)
else:
col_aliases = set()
if self.select: if self.select:
for col in self.select: for col in self.select:
if isinstance(col, (list, tuple)): if isinstance(col, (list, tuple)):
r = '%s.%s' % (qn(col[0]), qn(col[1])) r = '%s.%s' % (qn(col[0]), qn(col[1]))
result.append(r) if with_aliases and col[1] in col_aliases:
aliases.append(r) 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: else:
result.append(col.as_sql(quote_func=qn)) result.append(col.as_sql(quote_func=qn))
if hasattr(col, 'alias'): if hasattr(col, 'alias'):
aliases.append(col.alias) aliases.append(col.alias)
col_aliases.add(col.alias)
elif self.default_cols: elif self.default_cols:
cols = self.get_default_columns(True) cols = self.get_default_columns(True, with_aliases, col_aliases)
result.extend(cols) result.extend(cols)
aliases.extend(cols) aliases.extend(cols)
for table, col in self.related_select_cols: for table, col in self.related_select_cols:
r = '%s.%s' % (qn(table), qn(col)) r = '%s.%s' % (qn(table), qn(col))
result.append(r) if with_aliases and col in col_aliases:
aliases.append(r) 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) self._select_aliases = set(aliases)
return result 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 Computes the default columns for selecting every field in the base
model. Returns a list of default (alias, column) pairs suitable for model. Returns a list of default (alias, column) pairs suitable for
@ -406,7 +430,12 @@ class Query(object):
root_pk, model._meta.pk.column)) root_pk, model._meta.pk.column))
seen[model] = alias seen[model] = alias
if as_str: 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: else:
result.append((alias, field.column)) result.append((alias, field.column))
return result return result