result[0:0] = ['&'] # put an extra '&' in front
expected = 1
result.append(token)
- if isinstance(token, (list,tuple)): # domain term
+ if isinstance(token, (list, tuple)): # domain term
expected -= 1
else:
expected += op_arity.get(token, 0) - 1
def combine(operator, unit, zero, domains):
"""Returns a new domain expression where all domain components from ``domains``
- have been added together using the binary operator ``operator``.
+ have been added together using the binary operator ``operator``. The given
+ domains must be normalized.
:param unit: the identity element of the domains "set" with regard to the operation
performed by ``operator``, i.e the domain component ``i`` which, when
combined with any domain ``x`` via ``operator``, yields ``z``.
E.g. [(1,'=',1)] is the typical zero for OR_OPERATOR: as soon as
you see it in a domain component the resulting domain is the zero.
+ :param domains: a list of normalized domains.
"""
result = []
count = 0
and (((not internal) and element[1] in OPS) \
or (internal and element[1] in INTERNAL_OPS))
-def execute_recursive_in(cr, s, f, w, ids, op):
+def select_from_where(cr, s, f, w, ids, op):
# todo: merge into parent query as sub-query
res = []
if ids:
if op in ['<','>','>=','<=']:
- cr.execute('SELECT "%s"' \
- ' FROM "%s"' \
- ' WHERE "%s" %s %%s' % (s, f, w, op), (ids[0],))
- res.extend([r[0] for r in cr.fetchall()])
- else:
+ cr.execute('SELECT "%s" FROM "%s" WHERE "%s" %s %%s' % \
+ (s, f, w, op), (ids[0],)) # TODO shouldn't this be min/max(ids) ?
+ res = [r[0] for r in cr.fetchall()]
+ else: # TODO op is supposed to be 'in'? It is called with child_of...
for i in range(0, len(ids), cr.IN_MAX):
subids = ids[i:i+cr.IN_MAX]
- cr.execute('SELECT "%s"' \
- ' FROM "%s"' \
- ' WHERE "%s" IN %%s' % (s, f, w),(tuple(subids),))
+ cr.execute('SELECT "%s" FROM "%s" WHERE "%s" IN %%s' % \
+ (s, f, w), (tuple(subids),))
res.extend([r[0] for r in cr.fetchall()])
- else:
- cr.execute('SELECT distinct("%s")' \
- ' FROM "%s" where "%s" is not null' % (s, f, s)),
- res.extend([r[0] for r in cr.fetchall()])
return res
+def select_distinct_from_where_not_null(cr, s, f):
+ cr.execute('SELECT distinct("%s") FROM "%s" where "%s" is not null' % (s, f, s))
+ return [r[0] for r in cr.fetchall()]
+
class expression(object):
"""
parse a domain expression
def __init__(self, cr, uid, exp, table, context):
# check if the expression is valid
- if not reduce(lambda acc, val: acc and (is_operator(val) or is_leaf(val)), exp, True):
- raise ValueError('Bad domain expression: %r' % (exp,))
+ for x in exp:
+ if not (is_operator(x) or is_leaf(x)):
+ raise ValueError('Bad domain expression: %r, %r is not a valid operator or a valid term.' % (exp, x))
self.__field_tables = {} # used to store the table to use for the sql generation. key = index of the leaf
self.__all_tables = set()
self.__joins = []
# assign self.__exp with the normalized, parsed domain.
self.parse(cr, uid, normalize(exp), table, context)
+ # TODO used only for osv_memory
@property
def exp(self):
return self.__exp[:]
""" transform the leafs of the expression """
self.__exp = exp
- def _rec_get(right, table, parent=None, left='id', prefix=''):
- ids = child_of_right_to_ids(right, 'ilike')
+ def child_of_domain(left, right, table, parent=None, prefix=''):
+ ids = right
if table._parent_store and (not table.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)]
return [(left, 'in', rg(ids, table, parent or table._parent_name))]
# TODO rename this function as it is not strictly for 'child_of', but also for 'in'...
- def child_of_right_to_ids(value, operator):
+ def child_of_right_to_ids(value, operator, field_obj):
""" Normalize a single id, or a string, or a list of ids to a list of ids.
"""
if isinstance(value, basestring):
continue
left, operator, right = e
operator = operator.lower()
- working_table = table
- main_table = table
+ working_table = table # The table containing the field (the name provided in the left operand)
fargs = left.split('.', 1)
+
+ # If the field is _inherits'd, search for the working_table,
+ # and extract the field.
if fargs[0] in table._inherit_fields:
while True:
- field = main_table._columns.get(fargs[0])
+ field = working_table._columns.get(fargs[0])
if field:
self.__field_tables[i] = working_table
break
- working_table = main_table.pool.get(main_table._inherit_fields[fargs[0]][0])
- if working_table not in self.__all_tables:
- self.__joins.append('%s.%s=%s.%s' % (working_table._table, 'id', main_table._table, main_table._inherits[working_table._name]))
- self.__all_tables.add(working_table)
- main_table = working_table
+ next_table = working_table.pool.get(working_table._inherit_fields[fargs[0]][0])
+ if next_table not in self.__all_tables:
+ self.__joins.append('%s.%s=%s.%s' % (next_table._table, 'id', working_table._table, working_table._inherits[next_table._name]))
+ self.__all_tables.add(next_table)
+ working_table = next_table
+ # Or (try to) directly extract the field.
+ else:
+ field = working_table._columns.get(fargs[0])
- field = working_table._columns.get(fargs[0])
if not field:
if left == 'id' and operator == 'child_of':
- dom = _rec_get(right, working_table)
+ ids2 = child_of_right_to_ids(right, 'ilike', table)
+ dom = child_of_domain(left, ids2, working_table)
self.__exp = self.__exp[:i] + dom + self.__exp[i+1:]
continue
else:
self.__exp[i] = (fargs[0], 'in', right)
# Making search easier when there is a left operand as field.o2m or field.m2m
- if field._type in ['many2many','one2many']:
+ if field._type in ['many2many', 'one2many']:
right = field_obj.search(cr, uid, [(fargs[1], operator, right)], context=context)
- right1 = table.search(cr, uid, [(fargs[0],'in', right)], context=context)
+ right1 = table.search(cr, uid, [(fargs[0], 'in', right)], context=context)
if right1 == []:
self.__exp[i] = FALSE_LEAF
else:
self.__exp[i] = ('id', 'in', right1)
- if not isinstance(field,fields.property):
+ if not isinstance(field, fields.property):
continue
if field._properties and not field.store:
# Applying recursivity on field(one2many)
if operator == 'child_of':
if field._obj != working_table._name:
- dom = _rec_get(right, field_obj, left=left, prefix=field._obj)
+ ids2 = child_of_right_to_ids(right, 'ilike', field_obj)
+ dom = child_of_domain(left, ids2, field_obj, prefix=field._obj)
else:
- dom = _rec_get(right, working_table, parent=left)
+ ids2 = child_of_right_to_ids(right, 'ilike', field_obj)
+ dom = child_of_domain('id', ids2, working_table, parent=left)
self.__exp = self.__exp[:i] + dom + self.__exp[i+1:]
else:
if ids2:
operator = 'in'
else:
- if not isinstance(right,list):
+ if not isinstance(right, list):
ids2 = [right]
else:
ids2 = right
#no result found with given search criteria
call_null = False
self.__exp[i] = FALSE_LEAF
- else:
- call_null = True
- operator = 'in' # operator changed because ids are directly related to main object
else:
call_null = False
o2m_op = 'in'
if operator in ['not like','not ilike','not in','<>','!=']:
o2m_op = 'not in'
- self.__exp[i] = ('id', o2m_op, execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', ids2, operator))
+ self.__exp[i] = ('id', o2m_op, select_from_where(cr, field._fields_id, field_obj._table, 'id', ids2, operator))
if call_null:
o2m_op = 'not in'
if operator in ['not like','not ilike','not in','<>','!=']:
o2m_op = 'in'
- self.__exp[i] = ('id', o2m_op, execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', [], operator) or [0])
+ self.__exp[i] = ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, field_obj._table) or [0])
elif field._type == 'many2many':
#FIXME
def _rec_convert(ids):
if field_obj == table:
return ids
- return execute_recursive_in(cr, field._id1, field._rel, field._id2, ids, operator)
+ return select_from_where(cr, field._id1, field._rel, field._id2, ids, operator)
- dom = _rec_get(right, field_obj)
+ ids2 = child_of_right_to_ids(right, 'ilike', field_obj)
+ dom = child_of_domain('id', ids2, field_obj)
ids2 = field_obj.search(cr, uid, dom, context=context)
self.__exp[i] = ('id', 'in', _rec_convert(ids2))
else:
call_null_m2m = False
self.__exp[i] = FALSE_LEAF
else:
- call_null_m2m = True
operator = 'in' # operator changed because ids are directly related to main object
else:
call_null_m2m = False
m2m_op = 'in'
if operator in ['not like','not ilike','not in','<>','!=']:
m2m_op = 'not in'
+ self.__exp[i] = ('id', m2m_op, select_from_where(cr, field._id1, field._rel, field._id2, res_ids, operator) or [0])
- self.__exp[i] = ('id', m2m_op, execute_recursive_in(cr, field._id1, field._rel, field._id2, res_ids, operator) or [0])
if call_null_m2m:
m2m_op = 'not in'
if operator in ['not like','not ilike','not in','<>','!=']:
m2m_op = 'in'
- self.__exp[i] = ('id', m2m_op, execute_recursive_in(cr, field._id1, field._rel, field._id2, [], operator) or [0])
+ self.__exp[i] = ('id', m2m_op, select_distinct_from_where_not_null(cr, field._id1, field._rel) or [0])
elif field._type == 'many2one':
if operator == 'child_of':
if field._obj != working_table._name:
- dom = _rec_get(right, field_obj, left=left, prefix=field._obj)
+ ids2 = child_of_right_to_ids(right, 'ilike', field_obj)
+ dom = child_of_domain(left, ids2, field_obj, prefix=field._obj)
else:
- dom = _rec_get(right, working_table, parent=left)
+ ids2 = child_of_right_to_ids(right, 'ilike', field_obj)
+ dom = child_of_domain('id', ids2, working_table, parent=left)
self.__exp = self.__exp[:i] + dom + self.__exp[i+1:]
else:
- def _get_expression(field_obj,cr, uid, left, right, operator, context=None):
+ def _get_expression(field_obj, cr, uid, left, right, operator, context=None):
if context is None:
context = {}
c = context.copy()
operator = ( operator in ['<','>','<=','>='] ) and 'in' or operator
dict_op = {'not in':'!=','in':'=','=':'in','!=':'not in','<>':'not in'}
- if isinstance(right,tuple):
+ if isinstance(right, tuple):
right = list(right)
- if (not isinstance(right,list)) and operator in ['not in','in']:
+ if (not isinstance(right, list)) and operator in ['not in','in']:
operator = dict_op[operator]
- elif isinstance(right,list) and operator in ['<>','!=','=']: #for domain (FIELD,'=',['value1','value2'])
+ elif isinstance(right, list) and operator in ['<>','!=','=']: #for domain (FIELD,'=',['value1','value2'])
operator = dict_op[operator]
- res_ids = field_obj.name_search(cr, uid, right, [], operator, limit=None, context=c)
+ res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, limit=None, context=c)]
if not res_ids:
return FALSE_LEAF
else:
- right = map(lambda x: x[0], res_ids)
- return (left, 'in', right)
+ return (left, 'in', res_ids)
m2o_str = False
if right:
if isinstance(right, basestring): # and not isinstance(field, fields.related):
m2o_str = True
- elif isinstance(right,(list,tuple)):
+ elif isinstance(right, (list, tuple)):
m2o_str = True
for ele in right:
if not isinstance(ele, basestring):
if operator in ['not like','not ilike','not in','<>','!=']:
new_op = '!='
#Is it ok to put 'left' and not 'id' ?
- self.__exp[i] = (left,new_op,False)
+ self.__exp[i] = (left, new_op, False)
if m2o_str:
- self.__exp[i] = _get_expression(field_obj,cr, uid, left, right, operator, context=context)
+ self.__exp[i] = _get_expression(field_obj, cr, uid, left, right, operator, context=context)
else:
# other field type
# add the time part to datetime field when it's not there:
self.__exp[i] = ('id', 'inselect', (query1, query2))
def __leaf_to_sql(self, leaf, table):
- if leaf == TRUE_LEAF:
- return ('TRUE', [])
- if leaf == FALSE_LEAF:
- return ('FALSE', [])
left, operator, right = leaf
- if operator == 'inselect':
+ if leaf == TRUE_LEAF:
+ query = 'TRUE'
+ params = []
+
+ elif leaf == FALSE_LEAF:
+ query = 'FALSE'
+ params = []
+
+ elif operator == 'inselect':
query = '(%s.%s in (%s))' % (table._table, left, right[0])
params = right[1]
+
elif operator in ['in', 'not in']:
params = right and right[:] or []
len_before = len(params)
check_nulls = len_after != len_before
query = 'FALSE'
+ # TODO this code seems broken: 'not in [False]' will become 'true',
+ # i.e. 'not null or null', while I expect it to be 'not null'.
if len_after:
if left == 'id':
instr = ','.join(['%s'] * len_after)
query = '(%s.%s IS NOT NULL)' % (table._table, left)
if check_nulls:
query = '(%s OR %s.%s IS NULL)' % (query, table._table, left)
- else:
+
+ elif right == False and (left in table._columns) and table._columns[left]._type=="boolean" and (operator == '='):
+ query = '(%s.%s IS NULL or %s.%s = false )' % (table._table, left, table._table, left)
params = []
- if right == False and (leaf[0] in table._columns) and table._columns[leaf[0]]._type=="boolean" and (operator == '='):
- query = '(%s.%s IS NULL or %s.%s = false )' % (table._table, left,table._table, left)
- elif (((right == False) and (type(right)==bool)) or (right is None)) and (operator == '='):
- query = '%s.%s IS NULL ' % (table._table, left)
- elif right == False and (leaf[0] in table._columns) and table._columns[leaf[0]]._type=="boolean" and (operator in ['<>', '!=']):
- query = '(%s.%s IS NOT NULL and %s.%s != false)' % (table._table, left,table._table, left)
- elif (((right == False) and (type(right)==bool)) or right is None) and (operator in ['<>', '!=']):
- query = '%s.%s IS NOT NULL' % (table._table, left)
- elif (operator == '=?'):
- op = '='
- if (right is False or right is None):
- return ('TRUE', [])
- if left in table._columns:
- format = table._columns[left]._symbol_set[0]
- query = '(%s.%s %s %s)' % (table._table, left, op, format)
- params = table._columns[left]._symbol_set[1](right)
- else:
- query = "(%s.%s %s '%%s')" % (table._table, left, op)
- params = right
+ elif (((right == False) and (type(right)==bool)) or (right is None)) and (operator == '='):
+ query = '%s.%s IS NULL ' % (table._table, left)
+ params = []
+
+ elif right == False and (left in table._columns) and table._columns[left]._type=="boolean" and (operator in ['<>', '!=']):
+ query = '(%s.%s IS NOT NULL and %s.%s != false)' % (table._table, left, table._table, left)
+ params = []
+
+ elif (((right == False) and (type(right)==bool)) or right is None) and (operator in ['<>', '!=']):
+ query = '%s.%s IS NOT NULL' % (table._table, left)
+ params = []
+ elif (operator == '=?'):
+ if (right is False or right is None):
+ query = 'TRUE'
+ params = []
+ elif left in table._columns:
+ format = table._columns[left]._symbol_set[0]
+ query = '(%s.%s = %s)' % (table._table, left, format)
+ params = table._columns[left]._symbol_set[1](right)
else:
- if left == 'id':
- query = '%s.id %s %%s' % (table._table, operator)
- params = right
+ query = "(%s.%s = '%%s')" % (table._table, left)
+ params = right
+
+ elif left == 'id':
+ query = '%s.id %s %%s' % (table._table, operator)
+ params = right
+
+ else:
+ like = operator in ('like', 'ilike', 'not like', 'not ilike')
+
+ op = {'=like':'like','=ilike':'ilike'}.get(operator, operator)
+ if left in table._columns:
+ format = like and '%s' or table._columns[left]._symbol_set[0]
+ query = '(%s.%s %s %s)' % (table._table, left, op, format)
+ else:
+ query = "(%s.%s %s '%s')" % (table._table, left, op, right)
+
+ add_null = False
+ if like:
+ if isinstance(right, str):
+ str_utf8 = right
+ elif isinstance(right, unicode):
+ str_utf8 = right.encode('utf-8')
else:
- like = operator in ('like', 'ilike', 'not like', 'not ilike')
+ str_utf8 = str(right)
+ params = '%%%s%%' % str_utf8
+ add_null = not str_utf8
+ elif left in table._columns:
+ params = table._columns[left]._symbol_set[1](right)
- op = {'=like':'like','=ilike':'ilike'}.get(operator,operator)
- if left in table._columns:
- format = like and '%s' or table._columns[left]._symbol_set[0]
- query = '(%s.%s %s %s)' % (table._table, left, op, format)
- else:
- query = "(%s.%s %s '%s')" % (table._table, left, op, right)
-
- add_null = False
- if like:
- if isinstance(right, str):
- str_utf8 = right
- elif isinstance(right, unicode):
- str_utf8 = right.encode('utf-8')
- else:
- str_utf8 = str(right)
- params = '%%%s%%' % str_utf8
- add_null = not str_utf8
- elif left in table._columns:
- params = table._columns[left]._symbol_set[1](right)
-
- if add_null:
- query = '(%s OR %s.%s IS NULL)' % (query, table._table, left)
+ if add_null:
+ query = '(%s OR %s.%s IS NULL)' % (query, table._table, left)
if isinstance(params, basestring):
params = [params]