Before this commit, the query to fetch records with/without relations would be
done in a separate query which results would be then passed to the main query.
This commit adds this query as a subquery of the main query to do it in the
same SQL query instead of separately.
task-2211013
closesodoo/odoo#47729
Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
Signed-off-by: Romain Derie <rdeodoo@users.noreply.github.com>
Consider an x2many field `foo_ids` with `context={'active_test': False}`
in its definition, and a comodel with an active field. The value of the
field includes inactive records.
Now consider a search with a domain like `[('foo_ids.bar', op, value)]`.
The search should return all the records with corecords that satisfy the
domain `[('bar', op, value)]`, including inactive corecords, because the
field's context explicitly disables filtering on the active field.
closesodoo/odoo#43625
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
We optimize the search on domains like `[('line_ids', 'in', ids)]`.
The condition is rewritten `('id', 'in', ids1)` where `ids1` is the
result of
SELECT <many2one_field> FROM <comodel_table> WHERE id IN <ids>
The issue is that the latter potentially returns many duplicate values.
The fix consists in having as few duplicates as possible in `ids1`.
Note that domains like `[('line_ids.foo', '=', 42)]` implicitly benefit
from the optimization, as they are rewritten as the one above with
ids = comodel.search([('foo', '=', 42)]).ids
closesodoo/odoo#43574
X-original-commit: a13c05fa5df9c98504e8e1cdb09d8d4a8d13ea91
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
Ensure that expression.OR and expression.AND and some other
expression.py methods do not propagate or rely on TRUE_DOMAIN and
FALSE_DOMAIN that may be muted on some instance.
For example, if we did:
self.search(expression.OR([]))
then in the search method we do something like:
received_domain.append(('res_field', '=', False))
before this commit, FALSE_DOMAIN would be altered for any succeeding code
that try to use it in `[(0, '=', 1), ('res_field', '=', 'False')]`.
Without the changeset, the added test would fail with:
[(1, '=', 1), ('id', '=', 1)] != [(1, '=', 1)]
[(0, '=', 1), ('id', '=', 1)] != [(0, '=', 1)]
[(0, '=', 1), ('id', '=', 1)] != [(0, '=', 1)]
[(1, '=', 1), ('id', '=', 1)] != [(1, '=', 1)]
[(1, '=', 1), ('id', '=', 1)] != [(1, '=', 1)]
note: another commit referenced in #41968 should make the TRUE_DOMAIN
and FALSE_DOMAIN immutable.
related to work on opw-2154448
closes#42107closesodoo/odoo#42222
X-original-commit: 303ce32e6ccd7432b96ffae545e4a6eced68ae84
Signed-off-by: Nicolas Lempereur (nle) <nle@odoo.com>
`is_false` relies on no small part on equality tests between the input
triplets and either `TRUE_LEAF` or `FALSE_LEAF`. While these are
defined as tuples, RPC domains will always be lists (as neither
XML-RPC nor JSON have tuples, and their arrays deserialize to Python
lists).
This is an issue, because tuple and list never compare equal. As a
result, while the in / not in predicates can succeed, the TRUE_LEAF /
FALSE_LEAF never will, and thus domains which contain either and might
shortcut (avoid a query entirely) will always go through the entire
process.
Fix by having domain normalization also ensure all triplets are
tuples: that's the first thing `is_false` does, it should never cause
issues and could fix / improve / shortcut other routines.
Note: Also implements TRUE_LEAF and FALSE_LEAF handling in the
SSF's modifiers evaluator. And fixes the ValueError to work
correctly if it breaks on a tuple / dict.
closesodoo/odoo#39706
X-original-commit: a44f008b918ee66742f7e943b00e4ee3d7fe2b78
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
en_US may not be activated as it is possible to create a database in
another language using the database manager.
When trying to install a chart of account, the tax return entry tried
to format a date at the installation of the module, with no lang in
the context. The fallback was made on en_US but an error is raised if
that language is not activated.
As it is a very common scenario to retrieve a language from the
context, add a generic tool method to do it.
Replace and closesodoo/odoo#37629closesodoo/odoo#37568
Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
As odoo makes little difference, one would expect `(field, !=, 'foo')`
or `(field, 'not ilike', 'foo')` to have the same behaviour when field is
empty, and provide the opposite result as the "positive" operators.
However because NULL <OP> <value> returns NULL which is generally
assimilated to false, the sections above would fail to match records
whose field is null (they would match records whose field is *empty*
though).
Fix this by wrapping the operation in a coalesce falling back
to (hopefully) the correct value if the result is null.
See also: odoo/odoo#22879closesodoo/odoo#33959
Signed-off-by: Olivier Dony (odo) <odo@openerp.com>
Combining a list of elements containing only the monoid's unit should return the
unit.
Since the unit is skipped, it would return an empty domain instead of the unit,
meaning that (0, '=', 1) could not be used in a group rule.
To test this, create a record rule on a model M with domain (0, '=', 1)
that applies to group G.
Log in with a user in group G and access model M; you can read everything
(provided no other record rule applies) whereas you should see nothing.
Because of expression.AND(global_domains + [expression.OR(group_domains)])
we want the empty domain to still be left untouched, even if it violates
the OR's unit rules (i.e. OR(empty, OR(R)) = OR(R) <=> OR(empty) = unit).
Meaning that no group rule would reduce to having a (0, '=' 1) group rule.
So in the end we don't really care about all that algebraic nonsense.
closesodoo/odoo#28765
This commit replaces calls to pycompat helpers that were intended for
python 2 <-> python 3 interoperability for python 3 builtins, as python
2 is no longer officially supported by Odoo.
This includes:
* calls to imap/izip/ifilter replaced by map/zip/filter
* uses of text_type replaced by str
* uses of unichr replaced by chr
* calls to implements_to_string, implements_iterator removed
* string_types and integer_types replaced by str, int respectively
* calls to to_native replaced by calls to to_text
This is done in preparation to the removal of these deprecated helpers
in the following commit.
Before this commit, doing expression.OR() with only FALSE_LEAF would
yield [] which is equivalent to TRUE_LEAF and is therefore not correct.
The same happened (to a lesser extent) with expression.AND() within an
expression.OR(), since the former would return a [] which would be
ignored by expression.OR().
See tests for a clearer view of the use cases.
Fixes#30113, #26540closesodoo/odoo#31202
From this commit onwards, Date fields will return datetime.date objects and Datetime fields will return datetime.datetime objects, this implies a number of things that are clearly explained both in the ORM API for master.
This commit also introduces a number of helper functions for dates and datetimes that are exposed in tools.date_utils and fields.Date[time], explained in the documentation as well.
Task-ID: 47189
Before this rev:
* Take 2 domains that contain at least two leaves, and at least one of
them must use the implicit `&` operator
e.g.:
d1 = [('so_line', 'in', [91]), ('amount', '<=', 0.0)]
d2 = ['&', ('so_line', 'in', []), ('project_id', '!=', False)]
* Perform osv.expression.OR() between both domains
Expected result:
d3 = ['|', d1, d2]
Actual result (after normalization):
d3 = ['&', '|', d1, d2]
This is because, since the `&` is implicit for the first domain, when we
OR it, we give it an explicit `|` operator, so when we pass this domain
through the normalize_domain function, d1 no longer contains an implicit
`&` operator but instead the implicit operator is the one between d1 and
d2, therefore giving us a completely wrong domain.
The `combine` function states that it only accepts normalized domains,
however neither the OR nor AND functions do, this leads to a lot of
developers putting non-normalized domains into these functions, and
there's no error checking or anything that obviously indicates that the
domain is incorrect, so we might as well normalize all domains being
passed since it's already pretty optimized.
This replaces the former modified preorder tree traversal (MPTT) with the
fields `parent_left`/`parent_right`. Each record is associated to a string
`parent_path`, that represents the path from its root node to itself. The path
is made of the node ids suffixed with a slash:
a node | id | parent_path
/ \ a | 42 | 42/
... b b | 63 | 42/63/
/ \ c | 84 | 42/63/84/
c d d | 85 | 42/63/85/
This field provides an efficient implementation for parent_of/child_of queries:
the nodes in the subtree of record are the ones where `parent_path` starts with
the `parent_path` of record. It is also more efficient to maintain than the
MPTT fields, and less sensitive to concurrent updates, because the value of
`parent_path` does not depend on sibling nodes.
Make it easier to take advantage of subqueries whenever possible.
Also remove some weird semantics of the operator `not in`: searching with the
domain `[(F, 'not in', [])]` was returning the records with relations, instead
of all the records. The new semantics makes `[(F, 'not in', ids)]` equivalent
to the complement of `[(F, 'in', ids)]` in all cases.
Otherwise when the subquery is somehow injected in the parent
query (didn't find where but...) it's b-prefixed and treated as a
binary rather than a sub-query somehow, which fails.
* remove references to basestring & unicode (use relevant pycompat
helpers)
* remove some str calls (either entirely or replaced by relevant
helper, either text or native)
* use better API to avoid unnecessary conversions
* remove some XML declarations in views
In Python 3:
* various builtins and dict methods were changed to return
view/iterable objects rather than lists
* and the separate Python 2 view/iterable builtins and methods were
removed altogether
This is problematic when using these items as list (which the happens
repeatedly in Odoo), but more viciously when iterating *multiple times*
over them (which also happens, which I've messed up multiple times while
writing this, and which is a pain to debug even when you've just created
the issue).
Convert all code using these to semantics-matching cross-version
helper functions to get the LCD behaviour between P2 and P3, and
forbid the builtins via lint.
issue #8530
We sometimes use in Odoo a One2many field with an inverse Integer
field instead of a usual Many2one.
This allow for example in several instances to have a "Many2one" which
can be reference from several models, eg:
Model Ranking:
name = String field
res_id = Integer field
res_model = String field
Model Toy:
rank = One2many [inverse: Ranking -> res_id]
[domain: res_model == Toy]
Model Tool:
rank = One2many [inverse: Ranking -> res_id]
[domain: res_model == Tool]
This enable us to have a shared feature between otherwise unrelated models.
But there was several issue when searching on these One2many:
1) if the Integer Many2one was not stored (eg. it came from an inherits) on
the searched model, this could lead to an error.
2) when we searched:
- by IDs (rank in ['55']) with at least one id not respecting the domain
- by IDs with a negative operator
- with a negative operator on unfound string (rank != "no rank has this")
- with a false value (rank = False)
we would not apply the One2many domain (eg. res_model == Toy) and thus
possibly getting Toy 3 errenously because a Tool 3 was found without
the domain being applied.
This fix modify the search on One2many and for:
1. if the inverse is an Integer not stored field instead of Many2one
manage it.
2. if the field is an Integer field instead of Many2one and there is a
domain on the One2many: apply the domain on the inverse model found.
So only some search on One2many with a domain whose inverse field is an
Integer could be impacted.
This would also be nice to have for all One2many with a domain but the
probability of it being useful versus risk for performance is not judged
high enough.
opw-710508