[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
|
# 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):
|
def normalize_leaf(element):
|
||||||
""" Change a term's operator to some canonical form, simplifying later
|
""" Change a term's operator to some canonical form, simplifying later
|
||||||
processing. """
|
processing. """
|
||||||
|
@ -416,33 +447,12 @@ class ExtendedLeaf(object):
|
||||||
for item in self.context_stack:
|
for item in self.context_stack:
|
||||||
self._tables.append(item[0])
|
self._tables.append(item[0])
|
||||||
self._tables.append(table)
|
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
|
# check validity
|
||||||
self.check_leaf()
|
self.check_leaf()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '<osv.ExtendedLeaf: %s on %s (ctx: %s)>' % (str(self.leaf), self.table._table, ','.join(self._get_context_debug()))
|
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
|
# Join / Context manipulation
|
||||||
# running examples:
|
# running examples:
|
||||||
|
@ -488,42 +498,48 @@ class ExtendedLeaf(object):
|
||||||
# i.e.: many2one: 'state_id': current field name
|
# i.e.: many2one: 'state_id': current field name
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
|
|
||||||
def _generate_alias(self):
|
def generate_alias(self):
|
||||||
alias = self._tables[0]._table
|
links = [(context[1], context[4]) for context in self.context_stack]
|
||||||
for context in self.context_stack:
|
alias, alias_statement = generate_table_alias(self._tables[0], links)
|
||||||
alias += '__' + context[4]
|
|
||||||
return alias
|
return alias
|
||||||
|
|
||||||
def add_join_context(self, context_field_name, src_table_link_name, dst_table, dst_table_link_name):
|
def add_join_context(self, table, lhs_col, table_col, link):
|
||||||
""" See above comments for more details. A join context is a tuple structure
|
""" See above comments for more details. A join context is a tuple like:
|
||||||
holding the following elements: (src_table (self.table), src_table_link_name,
|
``(lhs, table, lhs_col, col, link)``
|
||||||
dst_table, dst_table_link_name, context_field_name)
|
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.
|
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.context_stack.append((self.table, table, lhs_col, table_col, link))
|
||||||
self._tables.append(dst_table)
|
self._tables.append(table)
|
||||||
self.table = dst_table
|
self.table = table
|
||||||
|
|
||||||
def get_join_conditions(self):
|
def get_join_conditions(self):
|
||||||
names = []
|
conditions = []
|
||||||
alias = self._tables[0]._table
|
alias = self._tables[0]._table
|
||||||
for context in self.context_stack:
|
for context in self.context_stack:
|
||||||
previous_alias = alias
|
previous_alias = alias
|
||||||
alias += '__' + context[4]
|
alias += '__' + context[4]
|
||||||
names.append('%s."%s"=%s."%s"' % (previous_alias, context[1], alias, context[3]))
|
conditions.append('"%s"."%s"="%s"."%s"' % (previous_alias, context[2], alias, context[3]))
|
||||||
return names
|
return conditions
|
||||||
|
|
||||||
def get_tables(self):
|
def get_tables(self):
|
||||||
tables = set()
|
tables = set()
|
||||||
alias = self._tables[0]._table
|
alias = self._tables[0]._table
|
||||||
for context in self.context_stack:
|
for context in self.context_stack:
|
||||||
alias += '__' + context[4]
|
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)
|
tables.add(table_full_alias)
|
||||||
return tables
|
return tables
|
||||||
|
|
||||||
def _get_context_debug(self):
|
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
|
return names
|
||||||
|
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
|
@ -542,12 +558,6 @@ class ExtendedLeaf(object):
|
||||||
if not is_operator(self.leaf) and not is_leaf(self.leaf, True):
|
if not is_operator(self.leaf) and not is_leaf(self.leaf, True):
|
||||||
raise ValueError("Invalid leaf %s" % str(self.leaf))
|
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):
|
def is_operator(self):
|
||||||
return is_operator(self.leaf)
|
return is_operator(self.leaf)
|
||||||
|
|
||||||
|
@ -598,54 +608,6 @@ class expression(object):
|
||||||
# parse the domain expression
|
# parse the domain expression
|
||||||
self.parse(cr, uid, context=context)
|
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
|
# Leafs management
|
||||||
# ----------------------------------------
|
# ----------------------------------------
|
||||||
|
@ -662,16 +624,6 @@ class expression(object):
|
||||||
tables.append(table_name)
|
tables.append(table_name)
|
||||||
return tables
|
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
|
# Parsing
|
||||||
# ----------------------------------------
|
# ----------------------------------------
|
||||||
|
@ -694,22 +646,78 @@ class expression(object):
|
||||||
:var obj relational_table: relational table of a field (field._obj)
|
:var obj relational_table: relational table of a field (field._obj)
|
||||||
ex: res_partner.bank_ids -> res_partner_bank
|
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
|
# Get the next leaf to process
|
||||||
leaf = stack.pop(0)
|
leaf = pop()
|
||||||
leafs_to_stack = []
|
|
||||||
results_to_stack = []
|
|
||||||
|
|
||||||
# Get working variables
|
# Get working variables
|
||||||
working_table = leaf.table
|
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():
|
if leaf.is_operator():
|
||||||
left, operator, right = leaf.leaf, None, None
|
left, operator, right = leaf.leaf, None, None
|
||||||
elif leaf.is_true_leaf() or leaf.is_false_leaf():
|
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])
|
left, operator, right = ('%s' % leaf.leaf[0], leaf.leaf[1], leaf.leaf[2])
|
||||||
else:
|
else:
|
||||||
left, operator, right = leaf.leaf
|
left, operator, right = leaf.leaf
|
||||||
# field_path = leaf.field_path
|
|
||||||
# field = leaf.field
|
|
||||||
field_path = left.split('.', 1)
|
field_path = left.split('.', 1)
|
||||||
field = working_table._columns.get(field_path[0])
|
field = working_table._columns.get(field_path[0])
|
||||||
|
|
||||||
if field and field._obj:
|
if field and field._obj:
|
||||||
relational_table = working_table.pool.get(field._obj)
|
relational_table = working_table.pool.get(field._obj)
|
||||||
else:
|
else:
|
||||||
|
@ -734,10 +739,8 @@ class expression(object):
|
||||||
# -> add directly to result
|
# -> add directly to result
|
||||||
# ----------------------------------------
|
# ----------------------------------------
|
||||||
|
|
||||||
if leaf.is_operator():
|
if leaf.is_operator() or leaf.is_true_leaf() or leaf.is_false_leaf():
|
||||||
results_to_stack.append(leaf)
|
push_result(leaf)
|
||||||
elif leaf.is_true_leaf() or leaf.is_false_leaf():
|
|
||||||
results_to_stack.append(leaf)
|
|
||||||
|
|
||||||
# ----------------------------------------
|
# ----------------------------------------
|
||||||
# FIELD NOT FOUND
|
# FIELD NOT FOUND
|
||||||
|
@ -755,16 +758,18 @@ class expression(object):
|
||||||
# { 'field_name': ('parent_model', 'm2o_field_to_reach_parent',
|
# { 'field_name': ('parent_model', 'm2o_field_to_reach_parent',
|
||||||
# field_column_obj, origina_parent_model), ... }
|
# field_column_obj, origina_parent_model), ... }
|
||||||
next_table = working_table.pool.get(working_table._inherit_fields[field_path[0]][0])
|
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')
|
leaf.add_join_context(next_table, working_table._inherits[next_table._name], 'id', working_table._inherits[next_table._name])
|
||||||
leafs_to_stack.append(leaf)
|
push(leaf)
|
||||||
|
|
||||||
elif not field and left == 'id' and operator == 'child_of':
|
elif not field and left == 'id' and operator == 'child_of':
|
||||||
ids2 = self.to_ids(cr, uid, right, working_table, context)
|
ids2 = to_ids(right, working_table, context)
|
||||||
dom = self.child_of_domain(cr, uid, left, ids2, working_table)
|
dom = child_of_domain(left, ids2, working_table)
|
||||||
leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom]
|
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:
|
elif not field and field_path[0] in MAGIC_COLUMNS:
|
||||||
results_to_stack.append(leaf)
|
push_result(leaf)
|
||||||
|
|
||||||
elif not field:
|
elif not field:
|
||||||
raise ValueError("Invalid field %r in leaf %r" % (left, str(leaf)))
|
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:
|
elif len(field_path) > 1 and field._type == 'many2one' and field._auto_join:
|
||||||
# res_partner.state_id = res_partner__state_id.id
|
# res_partner.state_id = res_partner__state_id.id
|
||||||
leaf.add_join_context(field_path[0], field_path[0], relational_table, 'id')
|
leaf.add_join_context(relational_table, field_path[0], 'id', field_path[0])
|
||||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table))
|
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:
|
elif len(field_path) > 1 and field._type == 'one2many' and field._auto_join:
|
||||||
# res_partner.id = res_partner__bank_ids.partner_id
|
# 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
|
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:
|
if domain:
|
||||||
domain = normalize_domain(domain)
|
domain = normalize_domain(domain)
|
||||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, AND_OPERATOR, relational_table))
|
for elem in reversed(domain):
|
||||||
for elem in domain:
|
push(create_substitution_leaf(leaf, elem, relational_table))
|
||||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, elem, relational_table))
|
push(create_substitution_leaf(leaf, AND_OPERATOR, 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))
|
|
||||||
|
|
||||||
elif len(field_path) > 1 and field._auto_join:
|
elif len(field_path) > 1 and field._auto_join:
|
||||||
raise NotImplementedError('_auto_join attribute not supported on many2many field %s' % (left))
|
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':
|
elif len(field_path) > 1 and field._type == 'many2one':
|
||||||
right_ids = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context)
|
right_ids = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context)
|
||||||
leaf.leaf = (field_path[0], 'in', right_ids)
|
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
|
# 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']:
|
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)
|
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))
|
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)
|
leaf.leaf = ('id', 'in', table_ids)
|
||||||
leafs_to_stack.append(leaf)
|
push(leaf)
|
||||||
|
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
# FUNCTION FIELD
|
# FUNCTION FIELD
|
||||||
|
@ -832,30 +836,31 @@ class expression(object):
|
||||||
# avoid compiling stack trace if not needed
|
# avoid compiling stack trace if not needed
|
||||||
if _logger.isEnabledFor(logging.DEBUG):
|
if _logger.isEnabledFor(logging.DEBUG):
|
||||||
_logger.debug(''.join(traceback.format_stack()))
|
_logger.debug(''.join(traceback.format_stack()))
|
||||||
leafs_to_stack.append(leaf)
|
push(leaf)
|
||||||
|
|
||||||
elif isinstance(field, fields.function) and not field.store:
|
elif isinstance(field, fields.function) and not field.store:
|
||||||
# this is a function field that is not stored
|
# this is a function field that is not stored
|
||||||
fct_domain = field.search(cr, uid, working_table, left, [leaf.leaf], context=context)
|
fct_domain = field.search(cr, uid, working_table, left, [leaf.leaf], context=context)
|
||||||
if not fct_domain:
|
if not fct_domain:
|
||||||
leaf.leaf = TRUE_LEAF
|
leaf.leaf = TRUE_LEAF
|
||||||
leafs_to_stack.append(leaf)
|
push(leaf)
|
||||||
else:
|
else:
|
||||||
# we assume that the expression is valid
|
# we assume that the expression is valid
|
||||||
# we create a dummy leaf for forcing the parsing of the resulting expression
|
# 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))
|
for domain_element in reversed(fct_domain):
|
||||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, TRUE_LEAF, working_table))
|
push(create_substitution_leaf(leaf, domain_element, working_table))
|
||||||
for domain_element in fct_domain:
|
# self.push(create_substitution_leaf(leaf, TRUE_LEAF, working_table))
|
||||||
leafs_to_stack.append(self.create_substitution_leaf(leaf, domain_element, working_table))
|
# self.push(create_substitution_leaf(leaf, AND_OPERATOR, working_table))
|
||||||
|
|
||||||
# Applying recursivity on field(one2many)
|
# Applying recursivity on field(one2many)
|
||||||
elif field._type == 'one2many' and operator == 'child_of':
|
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:
|
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:
|
else:
|
||||||
dom = self.child_of_domain(cr, uid, 'id', ids2, working_table, parent=left)
|
dom = child_of_domain('id', ids2, working_table, parent=left)
|
||||||
leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom]
|
for dom_leaf in reversed(dom):
|
||||||
|
push(create_substitution_leaf(leaf, dom_leaf, working_table))
|
||||||
|
|
||||||
elif field._type == 'one2many':
|
elif field._type == 'one2many':
|
||||||
call_null = True
|
call_null = True
|
||||||
|
@ -874,17 +879,17 @@ class expression(object):
|
||||||
if operator in ['like', 'ilike', 'in', '=']:
|
if operator in ['like', 'ilike', 'in', '=']:
|
||||||
#no result found with given search criteria
|
#no result found with given search criteria
|
||||||
call_null = False
|
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:
|
else:
|
||||||
ids2 = select_from_where(cr, field._fields_id, relational_table._table, 'id', ids2, operator)
|
ids2 = select_from_where(cr, field._fields_id, relational_table._table, 'id', ids2, operator)
|
||||||
if ids2:
|
if ids2:
|
||||||
call_null = False
|
call_null = False
|
||||||
o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
|
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:
|
if call_null:
|
||||||
o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in'
|
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':
|
elif field._type == 'many2many':
|
||||||
rel_table, rel_id1, rel_id2 = field._sql_names(working_table)
|
rel_table, rel_id1, rel_id2 = field._sql_names(working_table)
|
||||||
|
@ -895,10 +900,10 @@ class expression(object):
|
||||||
return ids
|
return ids
|
||||||
return select_from_where(cr, rel_id1, rel_table, rel_id2, ids, operator)
|
return select_from_where(cr, rel_id1, rel_table, rel_id2, ids, operator)
|
||||||
|
|
||||||
ids2 = self.to_ids(cr, uid, right, relational_table, context)
|
ids2 = to_ids(right, relational_table, context)
|
||||||
dom = self.child_of_domain(cr, uid, 'id', ids2, relational_table)
|
dom = child_of_domain('id', ids2, relational_table)
|
||||||
ids2 = relational_table.search(cr, uid, dom, context=context)
|
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:
|
else:
|
||||||
call_null_m2m = True
|
call_null_m2m = True
|
||||||
if right is not False:
|
if right is not False:
|
||||||
|
@ -915,26 +920,27 @@ class expression(object):
|
||||||
if operator in ['like', 'ilike', 'in', '=']:
|
if operator in ['like', 'ilike', 'in', '=']:
|
||||||
#no result found with given search criteria
|
#no result found with given search criteria
|
||||||
call_null_m2m = False
|
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:
|
else:
|
||||||
operator = 'in' # operator changed because ids are directly related to main object
|
operator = 'in' # operator changed because ids are directly related to main object
|
||||||
else:
|
else:
|
||||||
call_null_m2m = False
|
call_null_m2m = False
|
||||||
m2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
|
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:
|
if call_null_m2m:
|
||||||
m2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in'
|
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':
|
elif field._type == 'many2one':
|
||||||
if operator == 'child_of':
|
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:
|
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:
|
else:
|
||||||
dom = self.child_of_domain(cr, uid, 'id', ids2, working_table, parent=left)
|
dom = child_of_domain('id', ids2, working_table, parent=left)
|
||||||
leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom]
|
for dom_leaf in reversed(dom):
|
||||||
|
push(create_substitution_leaf(leaf, dom_leaf, working_table))
|
||||||
else:
|
else:
|
||||||
def _get_expression(relational_table, cr, uid, left, right, operator, context=None):
|
def _get_expression(relational_table, cr, uid, left, right, operator, context=None):
|
||||||
if context is None:
|
if context is None:
|
||||||
|
@ -958,10 +964,10 @@ class expression(object):
|
||||||
# resolve string-based m2o criterion into IDs
|
# resolve string-based m2o criterion into IDs
|
||||||
if isinstance(right, basestring) or \
|
if isinstance(right, basestring) or \
|
||||||
right and isinstance(right, (tuple, list)) and all(isinstance(item, basestring) for item in right):
|
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:
|
else:
|
||||||
# right == [] or right == False and all other cases are handled by __leaf_to_sql()
|
# right == [] or right == False and all other cases are handled by __leaf_to_sql()
|
||||||
results_to_stack.append(leaf)
|
push_result(leaf)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# other field type
|
# other field type
|
||||||
|
@ -973,7 +979,7 @@ class expression(object):
|
||||||
elif operator in ('<', '<='):
|
elif operator in ('<', '<='):
|
||||||
right += ' 23:59:59'
|
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:
|
elif field.translate:
|
||||||
need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike')
|
need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike')
|
||||||
|
@ -1008,30 +1014,15 @@ class expression(object):
|
||||||
right,
|
right,
|
||||||
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:
|
else:
|
||||||
results_to_stack.append(leaf)
|
push_result(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)
|
|
||||||
|
|
||||||
# ----------------------------------------
|
# ----------------------------------------
|
||||||
# END OF PARSING FULL DOMAIN
|
# END OF PARSING FULL DOMAIN
|
||||||
# ----------------------------------------
|
# ----------------------------------------
|
||||||
|
|
||||||
self.result = result
|
|
||||||
|
|
||||||
# Generate joins
|
# Generate joins
|
||||||
joins = set()
|
joins = set()
|
||||||
for leaf in self.result:
|
for leaf in self.result:
|
||||||
|
@ -1047,10 +1038,9 @@ class expression(object):
|
||||||
assert operator in (TERM_OPERATORS + ('inselect',)), \
|
assert operator in (TERM_OPERATORS + ('inselect',)), \
|
||||||
"Invalid operator %r in domain term %r" % (operator, leaf)
|
"Invalid operator %r in domain term %r" % (operator, leaf)
|
||||||
assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in table._all_columns \
|
assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in table._all_columns \
|
||||||
or left in MAGIC_COLUMNS, \
|
or left in MAGIC_COLUMNS, "Invalid field %r in domain term %r" % (left, leaf)
|
||||||
"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:
|
if leaf == TRUE_LEAF:
|
||||||
query = 'TRUE'
|
query = 'TRUE'
|
||||||
|
|
Loading…
Reference in New Issue