[IMP] [CLEAN] [REVIEW] expression: udpated domain management. Added pop and push methods in parse to hide the internal plumbery. Cleaned some comments, deleted dead/unnecessary code, updated some comments. to_ids and childof_domain back in parse. Added generate_table_alias, method that should be used everywhere if possible when generating table name or aliases for sql conditions and things like that.
bzr revid: tde@openerp.com-20121206162128-1qol201os1xztlz5
This commit is contained in:
parent
a0b8a58174
commit
bb57752f0a
|
@ -313,6 +313,37 @@ def distribute_not(domain):
|
|||
# Generic leaf manipulation
|
||||
# --------------------------------------------------
|
||||
|
||||
def _quote(to_quote):
|
||||
if '"' not in to_quote:
|
||||
return '"%s"' % to_quote
|
||||
return to_quote
|
||||
|
||||
|
||||
def generate_table_alias(src_model, joined_tables=[]):
|
||||
""" Generate a standard table alias name. An alias is generated as following:
|
||||
- the base is the source model table name
|
||||
- then, each joined table is added in the alias using a 'link field name'
|
||||
that is used to render unique aliases for a given path
|
||||
Examples:
|
||||
- src_model=res.users, join_tables=[]:
|
||||
alias = '"res_users"'
|
||||
- src_model=res.users, join_tables=[(res.partner, 'parent_id')]
|
||||
alias = '"res_partner" as "res_users__parent_id"'
|
||||
|
||||
:param model src_model: model source of the alias
|
||||
:param list join_tables: list of tuples
|
||||
(dst_model, link_field)
|
||||
|
||||
:return tuple: (table alias, alias statement for from clause with quotes added)
|
||||
"""
|
||||
alias = src_model._table
|
||||
if not joined_tables:
|
||||
return ('%s' % alias, '%s' % _quote(alias))
|
||||
for link in joined_tables:
|
||||
alias += '__' + link[1]
|
||||
return ('%s' % alias, '%s as %s' % (_quote(joined_tables[-1][0]._table), _quote(alias)))
|
||||
|
||||
|
||||
def normalize_leaf(element):
|
||||
""" Change a term's operator to some canonical form, simplifying later
|
||||
processing. """
|
||||
|
@ -416,33 +447,12 @@ class ExtendedLeaf(object):
|
|||
for item in self.context_stack:
|
||||
self._tables.append(item[0])
|
||||
self._tables.append(table)
|
||||
if self.is_operator():
|
||||
self.elements = self.leaf, None, None
|
||||
elif self.is_true_leaf() or self.is_false_leaf():
|
||||
# because we consider left as a string
|
||||
self.elements = ('%s' % leaf[0], leaf[1], leaf[2])
|
||||
else:
|
||||
self.elements = leaf
|
||||
self.field_path = self.elements[0].split('.', 1)
|
||||
self.field = self.table._columns.get(self.field_path[0])
|
||||
if self.field and self.field._obj:
|
||||
self.relational_table = self.table.pool.get(self.field._obj)
|
||||
else:
|
||||
self.relational_table = None
|
||||
# check validity
|
||||
self.check_leaf()
|
||||
|
||||
def __str__(self):
|
||||
return '<osv.ExtendedLeaf: %s on %s (ctx: %s)>' % (str(self.leaf), self.table._table, ','.join(self._get_context_debug()))
|
||||
|
||||
# def create_substitution_leaf(self, new_leaf, new_table=None):
|
||||
# if new_table is None:
|
||||
# new_table = self.table
|
||||
# return ExtendedLeaf(new_leaf, new_table, self.context_stack)
|
||||
|
||||
# def create_sibling_leaf(self, new_leaf):
|
||||
# pass
|
||||
|
||||
# --------------------------------------------------
|
||||
# Join / Context manipulation
|
||||
# running examples:
|
||||
|
@ -488,42 +498,48 @@ class ExtendedLeaf(object):
|
|||
# i.e.: many2one: 'state_id': current field name
|
||||
# --------------------------------------------------
|
||||
|
||||
def _generate_alias(self):
|
||||
alias = self._tables[0]._table
|
||||
for context in self.context_stack:
|
||||
alias += '__' + context[4]
|
||||
def generate_alias(self):
|
||||
links = [(context[1], context[4]) for context in self.context_stack]
|
||||
alias, alias_statement = generate_table_alias(self._tables[0], links)
|
||||
return alias
|
||||
|
||||
def add_join_context(self, context_field_name, src_table_link_name, dst_table, dst_table_link_name):
|
||||
""" See above comments for more details. A join context is a tuple structure
|
||||
holding the following elements: (src_table (self.table), src_table_link_name,
|
||||
dst_table, dst_table_link_name, context_field_name)
|
||||
def add_join_context(self, table, lhs_col, table_col, link):
|
||||
""" See above comments for more details. A join context is a tuple like:
|
||||
``(lhs, table, lhs_col, col, link)``
|
||||
where
|
||||
- lhs is the source table (self.table)
|
||||
- table is the destination table
|
||||
- lsh_col is the source table column name used for the condition
|
||||
- table_col is the destination table column name used for the condition
|
||||
- link is the field name source of the join used as context to
|
||||
generate the destination table alias
|
||||
|
||||
After adding the join, the table of the current leaf is updated.
|
||||
"""
|
||||
self.context_stack.append((self.table, src_table_link_name, dst_table, dst_table_link_name, context_field_name))
|
||||
self._tables.append(dst_table)
|
||||
self.table = dst_table
|
||||
self.context_stack.append((self.table, table, lhs_col, table_col, link))
|
||||
self._tables.append(table)
|
||||
self.table = table
|
||||
|
||||
def get_join_conditions(self):
|
||||
names = []
|
||||
conditions = []
|
||||
alias = self._tables[0]._table
|
||||
for context in self.context_stack:
|
||||
previous_alias = alias
|
||||
alias += '__' + context[4]
|
||||
names.append('%s."%s"=%s."%s"' % (previous_alias, context[1], alias, context[3]))
|
||||
return names
|
||||
conditions.append('"%s"."%s"="%s"."%s"' % (previous_alias, context[2], alias, context[3]))
|
||||
return conditions
|
||||
|
||||
def get_tables(self):
|
||||
tables = set()
|
||||
alias = self._tables[0]._table
|
||||
for context in self.context_stack:
|
||||
alias += '__' + context[4]
|
||||
table_full_alias = '"%s" as %s' % (context[2]._table, alias)
|
||||
table_full_alias = '"%s" as "%s"' % (context[1]._table, alias)
|
||||
tables.add(table_full_alias)
|
||||
return tables
|
||||
|
||||
def _get_context_debug(self):
|
||||
names = ['%s."%s"=%s."%s" (%s)' % (table[0]._table, table[1], table[2]._table, table[3], table[4]) for table in self.context_stack]
|
||||
names = ['"%s"."%s"="%s"."%s" (%s)' % (item[0]._table, item[2], item[1]._table, item[3], item[4]) for item in self.context_stack]
|
||||
return names
|
||||
|
||||
# --------------------------------------------------
|
||||
|
@ -542,12 +558,6 @@ class ExtendedLeaf(object):
|
|||
if not is_operator(self.leaf) and not is_leaf(self.leaf, True):
|
||||
raise ValueError("Invalid leaf %s" % str(self.leaf))
|
||||
|
||||
# if self.is_leaf() and not (self.is_true_leaf() or self.is_false_leaf()) \
|
||||
# and not self.field and not self.field_path[0] in self.table._inherit_fields \
|
||||
# and not (self.leaf[0] == 'id' and self.leaf[1] == 'child_of') \
|
||||
# and not (self.field_path[0] in MAGIC_COLUMNS):
|
||||
# raise ValueError("Invalid field %r in leaf %r" % (self.leaf[0], self.leaf))
|
||||
|
||||
def is_operator(self):
|
||||
return is_operator(self.leaf)
|
||||
|
||||
|
@ -598,54 +608,6 @@ class expression(object):
|
|||
# parse the domain expression
|
||||
self.parse(cr, uid, context=context)
|
||||
|
||||
# ----------------------------------------
|
||||
# Tools for domain manipulation
|
||||
# ----------------------------------------
|
||||
|
||||
def to_ids(self, cr, uid, value, relational_table, context=None, limit=None):
|
||||
""" Normalize a single id or name, or a list of those, into a list of ids
|
||||
:param {int,long,basestring,list,tuple} value:
|
||||
if int, long -> return [value]
|
||||
if basestring, convert it into a list of basestrings, then
|
||||
if list of basestring ->
|
||||
perform a name_search on relational_table for each name
|
||||
return the list of related ids
|
||||
"""
|
||||
names = []
|
||||
if isinstance(value, basestring):
|
||||
names = [value]
|
||||
elif value and isinstance(value, (tuple, list)) and all(isinstance(item, basestring) for item in value):
|
||||
names = value
|
||||
elif isinstance(value, (int, long)):
|
||||
return [value]
|
||||
if names:
|
||||
name_get_list = [name_get[0] for name in names for name_get in relational_table.name_search(cr, uid, name, [], 'ilike', context=context, limit=limit)]
|
||||
return list(set(name_get_list))
|
||||
return list(value)
|
||||
|
||||
def child_of_domain(self, cr, uid, left, ids, left_model, parent=None, prefix='', context=None):
|
||||
""" Return a domain implementing the child_of operator for [(left,child_of,ids)],
|
||||
either as a range using the parent_left/right tree lookup fields
|
||||
(when available), or as an expanded [(left,in,child_ids)] """
|
||||
if left_model._parent_store and (not left_model.pool._init):
|
||||
# TODO: Improve where joins are implemented for many with '.', replace by:
|
||||
# doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)]
|
||||
doms = []
|
||||
for o in left_model.browse(cr, uid, ids, context=context):
|
||||
if doms:
|
||||
doms.insert(0, OR_OPERATOR)
|
||||
doms += [AND_OPERATOR, ('parent_left', '<', o.parent_right), ('parent_left', '>=', o.parent_left)]
|
||||
if prefix:
|
||||
return [(left, 'in', left_model.search(cr, uid, doms, context=context))]
|
||||
return doms
|
||||
else:
|
||||
def recursive_children(ids, model, parent_field):
|
||||
if not ids:
|
||||
return []
|
||||
ids2 = model.search(cr, uid, [(parent_field, 'in', ids)], context=context)
|
||||
return ids + recursive_children(ids2, model, parent_field)
|
||||
return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))]
|
||||
|
||||
# ----------------------------------------
|
||||
# Leafs management
|
||||
# ----------------------------------------
|
||||
|
@ -662,16 +624,6 @@ class expression(object):
|
|||
tables.append(table_name)
|
||||
return tables
|
||||
|
||||
def create_substitution_leaf(self, leaf, new_elements, new_table=None):
|
||||
if new_table is None:
|
||||
new_table = leaf.table
|
||||
new_context_stack = [tuple(context) for context in leaf.context_stack]
|
||||
new_leaf = ExtendedLeaf(new_elements, new_table, context_stack=new_context_stack)
|
||||
return new_leaf
|
||||
|
||||
def create_sibling_leaf(self, leaf, new_elements):
|
||||
pass
|
||||
|
||||
# ----------------------------------------
|
||||
# Parsing
|
||||
# ----------------------------------------
|
||||
|
@ -694,22 +646,78 @@ class expression(object):
|
|||
:var obj relational_table: relational table of a field (field._obj)
|
||||
ex: res_partner.bank_ids -> res_partner_bank
|
||||
"""
|
||||
result = []
|
||||
stack = [ExtendedLeaf(leaf, self.root_table) for leaf in self.expression]
|
||||
|
||||
while stack:
|
||||
def to_ids(value, relational_table, context=None, limit=None):
|
||||
""" Normalize a single id or name, or a list of those, into a list of ids
|
||||
:param {int,long,basestring,list,tuple} value:
|
||||
if int, long -> return [value]
|
||||
if basestring, convert it into a list of basestrings, then
|
||||
if list of basestring ->
|
||||
perform a name_search on relational_table for each name
|
||||
return the list of related ids
|
||||
"""
|
||||
names = []
|
||||
if isinstance(value, basestring):
|
||||
names = [value]
|
||||
elif value and isinstance(value, (tuple, list)) and all(isinstance(item, basestring) for item in value):
|
||||
names = value
|
||||
elif isinstance(value, (int, long)):
|
||||
return [value]
|
||||
if names:
|
||||
name_get_list = [name_get[0] for name in names for name_get in relational_table.name_search(cr, uid, name, [], 'ilike', context=context, limit=limit)]
|
||||
return list(set(name_get_list))
|
||||
return list(value)
|
||||
|
||||
def child_of_domain(left, ids, left_model, parent=None, prefix='', context=None):
|
||||
""" Return a domain implementing the child_of operator for [(left,child_of,ids)],
|
||||
either as a range using the parent_left/right tree lookup fields
|
||||
(when available), or as an expanded [(left,in,child_ids)] """
|
||||
if left_model._parent_store and (not left_model.pool._init):
|
||||
# TODO: Improve where joins are implemented for many with '.', replace by:
|
||||
# doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)]
|
||||
doms = []
|
||||
for o in left_model.browse(cr, uid, ids, context=context):
|
||||
if doms:
|
||||
doms.insert(0, OR_OPERATOR)
|
||||
doms += [AND_OPERATOR, ('parent_left', '<', o.parent_right), ('parent_left', '>=', o.parent_left)]
|
||||
if prefix:
|
||||
return [(left, 'in', left_model.search(cr, uid, doms, context=context))]
|
||||
return doms
|
||||
else:
|
||||
def recursive_children(ids, model, parent_field):
|
||||
if not ids:
|
||||
return []
|
||||
ids2 = model.search(cr, uid, [(parent_field, 'in', ids)], context=context)
|
||||
return ids + recursive_children(ids2, model, parent_field)
|
||||
return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))]
|
||||
|
||||
def create_substitution_leaf(leaf, new_elements, new_table=None):
|
||||
if new_table is None:
|
||||
new_table = leaf.table
|
||||
new_context_stack = [tuple(context) for context in leaf.context_stack]
|
||||
new_leaf = ExtendedLeaf(new_elements, new_table, context_stack=new_context_stack)
|
||||
return new_leaf
|
||||
|
||||
def pop():
|
||||
return self.stack.pop()
|
||||
|
||||
def push(leaf):
|
||||
self.stack.append(leaf)
|
||||
|
||||
def push_result(leaf):
|
||||
self.result.append(leaf)
|
||||
|
||||
self.result = []
|
||||
self.stack = [ExtendedLeaf(leaf, self.root_table) for leaf in self.expression]
|
||||
# process from right to left; expression is from left to right
|
||||
self.stack.reverse()
|
||||
|
||||
while self.stack:
|
||||
# Get the next leaf to process
|
||||
leaf = stack.pop(0)
|
||||
leafs_to_stack = []
|
||||
results_to_stack = []
|
||||
leaf = pop()
|
||||
|
||||
# Get working variables
|
||||
working_table = leaf.table
|
||||
# left, operator, right = leaf.elements
|
||||
# field_path = leaf.field_path
|
||||
# field = leaf.field
|
||||
# relational_table = leaf.relational_table
|
||||
|
||||
if leaf.is_operator():
|
||||
left, operator, right = leaf.leaf, None, None
|
||||
elif leaf.is_true_leaf() or leaf.is_false_leaf():
|
||||
|
@ -717,11 +725,8 @@ class expression(object):
|
|||
left, operator, right = ('%s' % leaf.leaf[0], leaf.leaf[1], leaf.leaf[2])
|
||||
else:
|
||||
left, operator, right = leaf.leaf
|
||||
# field_path = leaf.field_path
|
||||
# field = leaf.field
|
||||
field_path = left.split('.', 1)
|
||||
field = working_table._columns.get(field_path[0])
|
||||
|
||||
if field and field._obj:
|
||||
relational_table = working_table.pool.get(field._obj)
|
||||
else:
|
||||
|
@ -734,10 +739,8 @@ class expression(object):
|
|||
# -> add directly to result
|
||||
# ----------------------------------------
|
||||
|
||||
if leaf.is_operator():
|
||||
results_to_stack.append(leaf)
|
||||
elif leaf.is_true_leaf() or leaf.is_false_leaf():
|
||||
results_to_stack.append(leaf)
|
||||
if leaf.is_operator() or leaf.is_true_leaf() or leaf.is_false_leaf():
|
||||
push_result(leaf)
|
||||
|
||||
# ----------------------------------------
|
||||
# FIELD NOT FOUND
|
||||
|
@ -755,16 +758,18 @@ class expression(object):
|
|||
# { 'field_name': ('parent_model', 'm2o_field_to_reach_parent',
|
||||
# field_column_obj, origina_parent_model), ... }
|
||||
next_table = working_table.pool.get(working_table._inherit_fields[field_path[0]][0])
|
||||
leaf.add_join_context(working_table._inherits[next_table._name], working_table._inherits[next_table._name], next_table, 'id')
|
||||
leafs_to_stack.append(leaf)
|
||||
leaf.add_join_context(next_table, working_table._inherits[next_table._name], 'id', working_table._inherits[next_table._name])
|
||||
push(leaf)
|
||||
|
||||
elif not field and left == 'id' and operator == 'child_of':
|
||||
ids2 = self.to_ids(cr, uid, right, working_table, context)
|
||||
dom = self.child_of_domain(cr, uid, left, ids2, working_table)
|
||||
leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom]
|
||||
ids2 = to_ids(right, working_table, context)
|
||||
dom = child_of_domain(left, ids2, working_table)
|
||||
for dom_leaf in reversed(dom):
|
||||
new_leaf = create_substitution_leaf(leaf, dom_leaf, working_table)
|
||||
push(new_leaf)
|
||||
|
||||
elif not field and field_path[0] in MAGIC_COLUMNS:
|
||||
results_to_stack.append(leaf)
|
||||
push_result(leaf)
|
||||
|
||||
elif not field:
|
||||
raise ValueError("Invalid field %r in leaf %r" % (left, str(leaf)))
|
||||
|
@ -784,20 +789,19 @@ class expression(object):
|
|||
|
||||
elif len(field_path) > 1 and field._type == 'many2one' and field._auto_join:
|
||||
# res_partner.state_id = res_partner__state_id.id
|
||||
leaf.add_join_context(field_path[0], field_path[0], relational_table, 'id')
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table))
|
||||
leaf.add_join_context(relational_table, field_path[0], 'id', field_path[0])
|
||||
push(create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table))
|
||||
|
||||
elif len(field_path) > 1 and field._type == 'one2many' and field._auto_join:
|
||||
# res_partner.id = res_partner__bank_ids.partner_id
|
||||
leaf.add_join_context(field_path[0], 'id', relational_table, field._fields_id)
|
||||
leaf.add_join_context(relational_table, 'id', field._fields_id, field_path[0])
|
||||
domain = field._domain(working_table) if callable(field._domain) else field._domain
|
||||
push(create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table))
|
||||
if domain:
|
||||
domain = normalize_domain(domain)
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, AND_OPERATOR, relational_table))
|
||||
for elem in domain:
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, elem, relational_table))
|
||||
print '--> appending %s' % str(leafs_to_stack[-1])
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table))
|
||||
for elem in reversed(domain):
|
||||
push(create_substitution_leaf(leaf, elem, relational_table))
|
||||
push(create_substitution_leaf(leaf, AND_OPERATOR, relational_table))
|
||||
|
||||
elif len(field_path) > 1 and field._auto_join:
|
||||
raise NotImplementedError('_auto_join attribute not supported on many2many field %s' % (left))
|
||||
|
@ -805,14 +809,14 @@ class expression(object):
|
|||
elif len(field_path) > 1 and field._type == 'many2one':
|
||||
right_ids = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context)
|
||||
leaf.leaf = (field_path[0], 'in', right_ids)
|
||||
leafs_to_stack.append(leaf)
|
||||
push(leaf)
|
||||
|
||||
# Making search easier when there is a left operand as field.o2m or field.m2m
|
||||
elif len(field_path) > 1 and field._type in ['many2many', 'one2many']:
|
||||
right_ids = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context)
|
||||
table_ids = working_table.search(cr, uid, [(field_path[0], 'in', right_ids)], context=dict(context, active_test=False))
|
||||
leaf.leaf = ('id', 'in', table_ids)
|
||||
leafs_to_stack.append(leaf)
|
||||
push(leaf)
|
||||
|
||||
# -------------------------------------------------
|
||||
# FUNCTION FIELD
|
||||
|
@ -832,30 +836,31 @@ class expression(object):
|
|||
# avoid compiling stack trace if not needed
|
||||
if _logger.isEnabledFor(logging.DEBUG):
|
||||
_logger.debug(''.join(traceback.format_stack()))
|
||||
leafs_to_stack.append(leaf)
|
||||
push(leaf)
|
||||
|
||||
elif isinstance(field, fields.function) and not field.store:
|
||||
# this is a function field that is not stored
|
||||
fct_domain = field.search(cr, uid, working_table, left, [leaf.leaf], context=context)
|
||||
if not fct_domain:
|
||||
leaf.leaf = TRUE_LEAF
|
||||
leafs_to_stack.append(leaf)
|
||||
push(leaf)
|
||||
else:
|
||||
# we assume that the expression is valid
|
||||
# we create a dummy leaf for forcing the parsing of the resulting expression
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, AND_OPERATOR, working_table))
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, TRUE_LEAF, working_table))
|
||||
for domain_element in fct_domain:
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, domain_element, working_table))
|
||||
for domain_element in reversed(fct_domain):
|
||||
push(create_substitution_leaf(leaf, domain_element, working_table))
|
||||
# self.push(create_substitution_leaf(leaf, TRUE_LEAF, working_table))
|
||||
# self.push(create_substitution_leaf(leaf, AND_OPERATOR, working_table))
|
||||
|
||||
# Applying recursivity on field(one2many)
|
||||
elif field._type == 'one2many' and operator == 'child_of':
|
||||
ids2 = self.to_ids(cr, uid, right, relational_table, context)
|
||||
ids2 = to_ids(right, relational_table, context)
|
||||
if field._obj != working_table._name:
|
||||
dom = self.child_of_domain(cr, uid, left, ids2, relational_table, prefix=field._obj)
|
||||
dom = child_of_domain(left, ids2, relational_table, prefix=field._obj)
|
||||
else:
|
||||
dom = self.child_of_domain(cr, uid, 'id', ids2, working_table, parent=left)
|
||||
leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom]
|
||||
dom = child_of_domain('id', ids2, working_table, parent=left)
|
||||
for dom_leaf in reversed(dom):
|
||||
push(create_substitution_leaf(leaf, dom_leaf, working_table))
|
||||
|
||||
elif field._type == 'one2many':
|
||||
call_null = True
|
||||
|
@ -874,17 +879,17 @@ class expression(object):
|
|||
if operator in ['like', 'ilike', 'in', '=']:
|
||||
#no result found with given search criteria
|
||||
call_null = False
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, FALSE_LEAF, working_table))
|
||||
push(create_substitution_leaf(leaf, FALSE_LEAF, working_table))
|
||||
else:
|
||||
ids2 = select_from_where(cr, field._fields_id, relational_table._table, 'id', ids2, operator)
|
||||
if ids2:
|
||||
call_null = False
|
||||
o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', o2m_op, ids2), working_table))
|
||||
push(create_substitution_leaf(leaf, ('id', o2m_op, ids2), working_table))
|
||||
|
||||
if call_null:
|
||||
o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in'
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)), working_table))
|
||||
push(create_substitution_leaf(leaf, ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)), working_table))
|
||||
|
||||
elif field._type == 'many2many':
|
||||
rel_table, rel_id1, rel_id2 = field._sql_names(working_table)
|
||||
|
@ -895,10 +900,10 @@ class expression(object):
|
|||
return ids
|
||||
return select_from_where(cr, rel_id1, rel_table, rel_id2, ids, operator)
|
||||
|
||||
ids2 = self.to_ids(cr, uid, right, relational_table, context)
|
||||
dom = self.child_of_domain(cr, uid, 'id', ids2, relational_table)
|
||||
ids2 = to_ids(right, relational_table, context)
|
||||
dom = child_of_domain('id', ids2, relational_table)
|
||||
ids2 = relational_table.search(cr, uid, dom, context=context)
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', 'in', _rec_convert(ids2)), working_table))
|
||||
push(create_substitution_leaf(leaf, ('id', 'in', _rec_convert(ids2)), working_table))
|
||||
else:
|
||||
call_null_m2m = True
|
||||
if right is not False:
|
||||
|
@ -915,26 +920,27 @@ class expression(object):
|
|||
if operator in ['like', 'ilike', 'in', '=']:
|
||||
#no result found with given search criteria
|
||||
call_null_m2m = False
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, FALSE_LEAF, working_table))
|
||||
push(create_substitution_leaf(leaf, FALSE_LEAF, working_table))
|
||||
else:
|
||||
operator = 'in' # operator changed because ids are directly related to main object
|
||||
else:
|
||||
call_null_m2m = False
|
||||
m2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]), working_table))
|
||||
push(create_substitution_leaf(leaf, ('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]), working_table))
|
||||
|
||||
if call_null_m2m:
|
||||
m2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in'
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)), working_table))
|
||||
push(create_substitution_leaf(leaf, ('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)), working_table))
|
||||
|
||||
elif field._type == 'many2one':
|
||||
if operator == 'child_of':
|
||||
ids2 = self.to_ids(cr, uid, right, relational_table, context)
|
||||
ids2 = to_ids(right, relational_table, context)
|
||||
if field._obj != working_table._name:
|
||||
dom = self.child_of_domain(cr, uid, left, ids2, relational_table, prefix=field._obj)
|
||||
dom = child_of_domain(left, ids2, relational_table, prefix=field._obj)
|
||||
else:
|
||||
dom = self.child_of_domain(cr, uid, 'id', ids2, working_table, parent=left)
|
||||
leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom]
|
||||
dom = child_of_domain('id', ids2, working_table, parent=left)
|
||||
for dom_leaf in reversed(dom):
|
||||
push(create_substitution_leaf(leaf, dom_leaf, working_table))
|
||||
else:
|
||||
def _get_expression(relational_table, cr, uid, left, right, operator, context=None):
|
||||
if context is None:
|
||||
|
@ -958,10 +964,10 @@ class expression(object):
|
|||
# resolve string-based m2o criterion into IDs
|
||||
if isinstance(right, basestring) or \
|
||||
right and isinstance(right, (tuple, list)) and all(isinstance(item, basestring) for item in right):
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, _get_expression(relational_table, cr, uid, left, right, operator, context=context), working_table))
|
||||
push(create_substitution_leaf(leaf, _get_expression(relational_table, cr, uid, left, right, operator, context=context), working_table))
|
||||
else:
|
||||
# right == [] or right == False and all other cases are handled by __leaf_to_sql()
|
||||
results_to_stack.append(leaf)
|
||||
push_result(leaf)
|
||||
|
||||
else:
|
||||
# other field type
|
||||
|
@ -973,7 +979,7 @@ class expression(object):
|
|||
elif operator in ('<', '<='):
|
||||
right += ' 23:59:59'
|
||||
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, (left, operator, right), working_table))
|
||||
push(create_substitution_leaf(leaf, (left, operator, right), working_table))
|
||||
|
||||
elif field.translate:
|
||||
need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike')
|
||||
|
@ -1008,30 +1014,15 @@ class expression(object):
|
|||
right,
|
||||
right,
|
||||
]
|
||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', 'inselect', (subselect, params)), working_table))
|
||||
push(create_substitution_leaf(leaf, ('id', 'inselect', (subselect, params)), working_table))
|
||||
|
||||
else:
|
||||
results_to_stack.append(leaf)
|
||||
|
||||
# ----------------------------------------
|
||||
# END OF PROCESS OF CURRENT LEAF
|
||||
# -> results_to_stack elements are added in result
|
||||
# -> leafs_to_stack elements are inserted back in the processed
|
||||
# stack to be immediately processed
|
||||
# ----------------------------------------
|
||||
|
||||
leafs_to_stack.reverse()
|
||||
for leaf in results_to_stack:
|
||||
result.append(leaf)
|
||||
for leaf in leafs_to_stack:
|
||||
stack.insert(0, leaf)
|
||||
push_result(leaf)
|
||||
|
||||
# ----------------------------------------
|
||||
# END OF PARSING FULL DOMAIN
|
||||
# ----------------------------------------
|
||||
|
||||
self.result = result
|
||||
|
||||
# Generate joins
|
||||
joins = set()
|
||||
for leaf in self.result:
|
||||
|
@ -1047,10 +1038,9 @@ class expression(object):
|
|||
assert operator in (TERM_OPERATORS + ('inselect',)), \
|
||||
"Invalid operator %r in domain term %r" % (operator, leaf)
|
||||
assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in table._all_columns \
|
||||
or left in MAGIC_COLUMNS, \
|
||||
"Invalid field %r in domain term %r" % (left, leaf)
|
||||
or left in MAGIC_COLUMNS, "Invalid field %r in domain term %r" % (left, leaf)
|
||||
|
||||
table_alias = '"%s"' % (eleaf._generate_alias())
|
||||
table_alias = '"%s"' % (eleaf.generate_alias())
|
||||
|
||||
if leaf == TRUE_LEAF:
|
||||
query = 'TRUE'
|
||||
|
|
Loading…
Reference in New Issue