switch for forcibly disabling explicit joins, and partial work for merging...

switch for forcibly disabling explicit joins, and partial work for merging related-key join-chains based on aliases

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/catalog_join@42594 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 5d17b17c
......@@ -60,7 +60,8 @@ class ColumnMap(object):
def __init__(self,
catalog_table_name=None,
table_override_map=None,
left_join_list=None):
left_join_list=None,
implicit_join=False):
self.catalog_table_name = catalog_table_name
# Key: group
# Value: set of column names
......@@ -104,6 +105,10 @@ class ColumnMap(object):
# We need to keep track of the original definition to do inner joins on it
self._inner_table_definition = self.table_definition
self.left_join_list = left_join_list
self.implicit_join = implicit_join
assert not (self.implicit_join and self.left_join_list), (
"Cannot do left_joins while forcing implicit join"
)
@profiler_decorator
def registerColumn(self, raw_column, group=DEFAULT_GROUP_ID, simple_query=None):
......@@ -478,15 +483,19 @@ class ColumnMap(object):
def _isBackwardCompatibilityRequired(self):
return bool(
# if they explicitly ask for implicit
self.implicit_join or
# if they don't pass a catalog alias, we cannot do explicit joins
not self._setMinimalTableDefinition() or
# If one or more RelatedKey methods weren't converted, we'll get
# queries for an implicit inner join, so we have to do all joins
# as implicit.
self.join_query_list
self.join_query_list or
# for now, work in BW compat mode if a table_override
# is passed. It only works for simple subselect
# definitions anyway, and it's being used primarily
# for writing left-joins manually.
or self.table_override_map)
self.table_override_map)
def getTableAliasDict(self):
if self._isBackwardCompatibilityRequired():
......@@ -620,19 +629,21 @@ class ColumnMap(object):
try:
catalog_table_alias = self.getCatalogTableAlias()
except KeyError:
LOG('ColumnMap', WARNING,
'_setMinimalTableDefinition called but the main catalog has not '
'yet received an alias!')
return False
inner_def.replace(self.makeTableAliasDefinition(self.catalog_table_name,
catalog_table_alias))
return True
def getTableDefinition(self):
if not self._setMinimalTableDefinition():
raise RuntimeError("ColumnMap.build() must be called first!")
if self._isBackwardCompatibilityRequired():
# BBB: One of the RelatedKeys registered an implicit join, do
# not return a table definition, self.getTableAliasDict() should
# be used instead
return None
self.table_definition.checkTableAliases()
return self.table_definition
def addRelatedKeyJoin(self, column, right_side, condition):
......
......@@ -64,7 +64,8 @@ class EntireQuery(object):
catalog_table_name=None,
extra_column_list=(),
from_expression=None,
order_by_override_list=None):
order_by_override_list=None,
implicit_join=False):
self.query = query
self.order_by_list = list(order_by_list)
self.order_by_override_set = frozenset(order_by_override_list)
......@@ -75,6 +76,7 @@ class EntireQuery(object):
self.catalog_table_name = catalog_table_name
self.extra_column_list = list(extra_column_list)
self.from_expression = from_expression
self.implicit_join = implicit_join
def asSearchTextExpression(self, sql_catalog):
return self.query.asSearchTextExpression(sql_catalog)
......@@ -89,6 +91,7 @@ class EntireQuery(object):
column_map = ColumnMap(catalog_table_name=self.catalog_table_name,
table_override_map=self.from_expression,
left_join_list=self.left_join_list,
implicit_join=self.implicit_join,
)
self.column_map = column_map
for extra_column in self.extra_column_list:
......
......@@ -2298,6 +2298,8 @@ class Catalog(Folder,
select_dict = dict([(x, None) for x in select_dict])
# Handle left_join_list
left_join_list = kw.pop('left_join_list', ())
# Handle implicit_join
implicit_join = kw.pop('implicit_join', False)
# Handle order_by_list
order_by_list = kw.pop('order_by_list', None)
sort_on = kw.pop('sort_on', None)
......@@ -2336,6 +2338,7 @@ class Catalog(Folder,
group_by_list=group_by_list,
select_dict=select_dict,
left_join_list=left_join_list,
implicit_join=implicit_join,
limit=limit,
catalog_table_name=query_table,
extra_column_list=extra_column_list,
......
......@@ -181,8 +181,6 @@ class SQLExpression(object):
sql_expression.query,
', '.join('%r (%r)' % (x, x.query) for x in self.sql_expression_list))
raise ValueError, message
if result is not None:
result.checkTableAliases()
return result
@profiler_decorator
......
......@@ -78,6 +78,14 @@ class TableDefinition(object):
def _extendJoinConditionQueryList(self, query_list):
raise NotImplementedError('should be implemented by subclasses')
def getSuperSet(self, other):
"""Checks if this TableDefinition is a subset of the other table
definition or vice-versa.
Returns whichever is the superset of the other or None
"""
raise NotImplementedError('should be implemented by subclasses')
class PlaceHolderTableDefinition(TableDefinition):
"""Table Definition that simply holds an inner table definition and
delegates to it the rendering.
......@@ -105,8 +113,14 @@ class PlaceHolderTableDefinition(TableDefinition):
return self.table_definition.render()
def _extendJoinConditionQueryList(self, query_list):
# XXX _extendJoinConditionQueryList
#assert self.table_definition is not None, "table definition wasn't set"
if self.table_definition is not None:
return self.table_definition._extendJoinConditionQueryList(query_list)
def getSuperSet(self, other):
assert self.table_definition is not None, "table definition wasn't set"
return self.table_definition._extendJoinConditionQueryList(query_list)
return self.table_definition.getSuperSet(other)
class TableAlias(TableDefinition):
"""Definition of a table alias as a FROM expression"""
......@@ -139,6 +153,22 @@ class TableAlias(TableDefinition):
def _extendJoinConditionQueryList(self, query_list):
pass
def __eq__(self, other):
return (isinstance(other, TableAlias) and
self.table == other.table and
self.alias == other.alias)
def getSuperSet(self, other):
"""A TableAlias is a subset of another table Alias if either:
- the other is an equivalent TableAlias
- the other is an InnerJoin where the left-side is an equivalent TableAlias
"""
if isinstance(other, TableAlias) and self == other:
# we're just like the other guy, we could return self or other
return self
# delegate the rest of the job to InnerJoin
return other.getSuperSet(self)
JOIN_FORMAT = """
(
%(left)s
......@@ -149,17 +179,18 @@ JOIN_FORMAT = """
)
""".strip()
class InnerJoin(TableDefinition):
"""Definition of an inner-join as a FROM expression"""
class Join(TableDefinition):
JOIN_TYPE = "INNER JOIN"
JOIN_TYPE = None
def __init__(self, left_tabledef, right_tabledef, condition):
assert self.JOIN_TYPE, ('Join must be subclassed and self.JOIN_TYPE '
'must be defined.')
assert isinstance(left_tabledef, (TableDefinition, None.__class__))
assert isinstance(right_tabledef, (TableDefinition, None.__class__))
self.left_tabledef = left_tabledef
self.right_tabledef = right_tabledef
# perhaps expect condition to be a SQLExpression?
# perhaps assert condition is an SQLExpression?
self.condition = condition
def render(self):
......@@ -187,6 +218,37 @@ class InnerJoin(TableDefinition):
self.right_tabledef._extendJoinConditionQueryList(query_list)
query_list.append(SQLQuery(self.condition))
def getSuperSet(self, other):
return None
class InnerJoin(Join):
"""Definition of an inner-join as a FROM expression"""
JOIN_TYPE = "INNER JOIN"
def getSuperSet(self, other):
"""This InnerJoin is a superset of another TableDefinition if either:
- other is a TableAlias (or None) equal to our
left_side. I.e. "other" is at the end of it's inner-join chain.
- other is an InnerJoin, and it's left-side is equal to our left-side (both TableAliases or None), and it's righ
"""
if self.left_tabledef == other:
# other and left-side are both None or TableAliases
return self
if (isinstance(other, InnerJoin) and
self.left_tabledef == other.left_tabledef):
# our left-sides match. If one of our right sides is a superset of the
# other, then one of is is the superset
sub_superset = self.right_tabledef.getSuperSet(other.right_tabledef)
if sub_superset is self.right_tabledef:
return self
elif sub_superset is other.right_tabledef:
return other
return None
return None
class LeftJoin(InnerJoin):
"""Definition of a left-join as a FROM expression"""
......@@ -210,9 +272,6 @@ class LegacyTableDefinition(TableDefinition):
self.from_expression = from_expression
self.table_alias_map = table_alias_map
def checkTableAliases(self, current_aliases=None):
pass
def render(self):
from_expression_dict = self.from_expression
table_alias_map = self.table_alias_map
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment