summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql_util.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2007-02-01 01:47:54 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2007-02-01 01:47:54 +0000
commit72f479b324c0b62a19e58c7b173a62b55c34a928 (patch)
tree537353c7423a1be7556300cd7f3a059a924a68d1 /lib/sqlalchemy/sql_util.py
parent7a429e2bb60939fb939d07014d5c3b7c020fdcbf (diff)
downloadsqlalchemy-72f479b324c0b62a19e58c7b173a62b55c34a928.tar.gz
- improved support for complex queries embedded into "where" criterion
for query.select() [ticket:449] - contains_eager('foo') automatically implies eagerload('foo') - query.options() can take a combiantion MapperOptions and tuples of MapperOptions, so that functions can return groups - refactoring to Aliasizer and ClauseAdapter so that they share a common base methodology, which addresses all sql.ColumnElements instead of just schema.Column. common list-processing methods added. - query.compile and eagerloader._aliasize_orderby make usage of improved list processing on above. - query.compile, within the "nested select generate" step processes the order_by clause using the ClauseAdapter instead of Aliasizer since there is only one "target"
Diffstat (limited to 'lib/sqlalchemy/sql_util.py')
-rw-r--r--lib/sqlalchemy/sql_util.py89
1 files changed, 56 insertions, 33 deletions
diff --git a/lib/sqlalchemy/sql_util.py b/lib/sqlalchemy/sql_util.py
index 10d4495d9..e672feb1a 100644
--- a/lib/sqlalchemy/sql_util.py
+++ b/lib/sqlalchemy/sql_util.py
@@ -78,8 +78,53 @@ class ColumnFinder(sql.ClauseVisitor):
self.columns.add(c)
def __iter__(self):
return iter(self.columns)
-
-class Aliasizer(sql.ClauseVisitor):
+
+class ColumnsInClause(sql.ClauseVisitor):
+ """given a selectable, visits clauses and determines if any columns from the clause are in the selectable"""
+ def __init__(self, selectable):
+ self.selectable = selectable
+ self.result = False
+ def visit_column(self, column):
+ if self.selectable.c.get(column.key) is column:
+ self.result = True
+
+class AbstractClauseProcessor(sql.ClauseVisitor):
+ """traverses a clause and attempts to convert the contents of container elements
+ to a converted element. the conversion operation is defined by subclasses."""
+ def convert_element(self, elem):
+ """define the 'conversion' method for this AbstractClauseProcessor"""
+ raise NotImplementedError()
+ def copy_and_process(self, list_):
+ """copy the container elements in the given list to a new list and
+ process the new list."""
+ list_ = [o.copy_container() for o in list_]
+ self.process_list(list_)
+ return list_
+
+ def process_list(self, list_):
+ """process all elements of the given list in-place"""
+ for i in range(0, len(list_)):
+ elem = self.convert_element(list_[i])
+ if elem is not None:
+ list_[i] = elem
+ else:
+ list_[i].accept_visitor(self)
+ def visit_compound(self, compound):
+ self.visit_clauselist(compound)
+ def visit_clauselist(self, clist):
+ for i in range(0, len(clist.clauses)):
+ n = self.convert_element(clist.clauses[i])
+ if n is not None:
+ clist.clauses[i] = n
+ def visit_binary(self, binary):
+ elem = self.convert_element(binary.left)
+ if elem is not None:
+ binary.left = elem
+ elem = self.convert_element(binary.right)
+ if elem is not None:
+ binary.right = elem
+
+class Aliasizer(AbstractClauseProcessor):
"""converts a table instance within an expression to be an alias of that table."""
def __init__(self, *tables, **kwargs):
self.tables = {}
@@ -95,21 +140,13 @@ class Aliasizer(sql.ClauseVisitor):
self.binary = None
def get_alias(self, table):
return self.aliases[table]
- def visit_compound(self, compound):
- self.visit_clauselist(compound)
- def visit_clauselist(self, clist):
- for i in range(0, len(clist.clauses)):
- if isinstance(clist.clauses[i], schema.Column) and self.tables.has_key(clist.clauses[i].table):
- orig = clist.clauses[i]
- clist.clauses[i] = self.get_alias(clist.clauses[i].table).corresponding_column(clist.clauses[i])
- def visit_binary(self, binary):
- if isinstance(binary.left, schema.Column) and self.tables.has_key(binary.left.table):
- binary.left = self.get_alias(binary.left.table).corresponding_column(binary.left)
- if isinstance(binary.right, schema.Column) and self.tables.has_key(binary.right.table):
- binary.right = self.get_alias(binary.right.table).corresponding_column(binary.right)
-
+ def convert_element(self, elem):
+ if isinstance(elem, sql.ColumnElement) and hasattr(elem, 'table') and self.tables.has_key(elem.table):
+ return self.get_alias(elem.table).corresponding_column(elem)
+ else:
+ return None
-class ClauseAdapter(sql.ClauseVisitor):
+class ClauseAdapter(AbstractClauseProcessor):
"""given a clause (like as in a WHERE criterion), locates columns which 'correspond' to a given selectable,
and changes those columns to be that of the selectable.
@@ -140,7 +177,8 @@ class ClauseAdapter(sql.ClauseVisitor):
self.include = include
self.exclude = exclude
self.equivalents = equivalents
- def include_col(self, col):
+
+ def convert_element(self, col):
if not isinstance(col, sql.ColumnElement):
return None
if self.include is not None:
@@ -153,19 +191,4 @@ class ClauseAdapter(sql.ClauseVisitor):
if newcol is None and self.equivalents is not None and col in self.equivalents:
newcol = self.selectable.corresponding_column(self.equivalents[col], raiseerr=False, keys_ok=False)
return newcol
- def visit_binary(self, binary):
- col = self.include_col(binary.left)
- if col is not None:
- binary.left = col
- col = self.include_col(binary.right)
- if col is not None:
- binary.right = col
-
-class ColumnsInClause(sql.ClauseVisitor):
- """given a selectable, visits clauses and determines if any columns from the clause are in the selectable"""
- def __init__(self, selectable):
- self.selectable = selectable
- self.result = False
- def visit_column(self, column):
- if self.selectable.c.get(column.key) is column:
- self.result = True
+