diff options
| author | Raphael Collet <rco@odoo.com> | 2020-05-05 12:56:23 +0000 |
|---|---|---|
| committer | Raphael Collet <rco@odoo.com> | 2020-05-20 07:58:59 +0000 |
| commit | dda73afe13f726858876ae4c1b58f1550453d802 (patch) | |
| tree | 127ac2a24a7671b8f69d54774cf29dc0008e1293 /odoo/osv | |
| parent | aed2134b6388258e907f8cb0dbd53857fb32d5c4 (diff) | |
[REF] core: avoid query.tables and replacements in query strings
Instead of merging Query objects (main domain and access rules domains),
which requires renaming aliases in query strings, make `expression` push
its result in an existing Query object. This removes tricky code.
This also avoids using `IrRule.domain_get()`, which has become unsafe,
since it returns a list of tables (for the FROM clause) which does not
include the joins from the generated Query object.
closes odoo/odoo#49999
Related: odoo/enterprise#10126
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
Diffstat (limited to 'odoo/osv')
| -rw-r--r-- | odoo/osv/expression.py | 9 | ||||
| -rw-r--r-- | odoo/osv/query.py | 30 |
2 files changed, 27 insertions, 12 deletions
diff --git a/odoo/osv/expression.py b/odoo/osv/expression.py index 95edcecb023..eba82d9950c 100644 --- a/odoo/osv/expression.py +++ b/odoo/osv/expression.py @@ -478,12 +478,14 @@ class expression(object): For more info: http://christophe-simonis-at-tiny.blogspot.com/2008/08/new-new-domain-notation.html """ - def __init__(self, domain, model): + def __init__(self, domain, model, alias=None, query=None): """ Initialize expression object and automatically parse the expression right after initialization. :param domain: expression (using domain ('foo', '=', 'bar') format) :param model: root model + :param alias: alias for the model table if query is provided + :param query: optional query object holding the final result :attr root_model: base model for the query :attr expression: the domain to parse, normalized and prepared @@ -492,12 +494,13 @@ class expression(object): """ self._unaccent = get_unaccent_wrapper(model._cr) self.root_model = model + self.root_alias = alias or model._table # normalize and prepare the expression for parsing self.expression = distribute_not(normalize_domain(domain)) # this object handles all the joins - self.query = Query(['"%s"' % model._table]) + self.query = Query(['"%s"' % model._table]) if query is None else query # parse the domain expression self.parse() @@ -651,7 +654,7 @@ class expression(object): # the form: (leaf, corresponding model, corresponding table alias) stack = [] for leaf in self.expression: - push(leaf, self.root_model, self.root_model._table) + push(leaf, self.root_model, self.root_alias) # stack of SQL expressions in the form: (expr, params) result_stack = [] diff --git a/odoo/osv/query.py b/odoo/osv/query.py index d21b340d26d..79b7a8424ff 100644 --- a/odoo/osv/query.py +++ b/odoo/osv/query.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import warnings def _quote(to_quote): @@ -25,7 +26,7 @@ class Query(object): # holds the list of tables joined using default JOIN. # the table names are stored double-quoted (backwards compatibility) - self.tables = tables or [] + self._tables = tables or [] # holds the list of WHERE clause elements, to be joined with # 'AND' when generating the final query @@ -64,14 +65,25 @@ class Query(object): # ... self.extras = extras or {} + def add_table(self, table): + table = _quote(table) + assert table not in self._tables, '%s already in %s' % (table, self) + self._tables.append(table) + + @property + def tables(self): + warnings.warn("deprecated Query.tables, use Query.get_sql() instead", + DeprecationWarning) + return self._tables + def _get_table_aliases(self): from odoo.osv.expression import get_alias_from_query - return [get_alias_from_query(from_statement)[1] for from_statement in self.tables] + return [get_alias_from_query(from_statement)[1] for from_statement in self._tables] def _get_alias_mapping(self): from odoo.osv.expression import get_alias_from_query mapping = {} - for table in self.tables: + for table in self._tables: alias, statement = get_alias_from_query(table) mapping[statement] = table return mapping @@ -113,8 +125,8 @@ class Query(object): alias, alias_statement = generate_table_alias(lhs, [(table, link)]) if implicit: - if alias_statement not in self.tables: - self.tables.append(alias_statement) + if alias_statement not in self._tables: + self._tables.append(alias_statement) condition = '("%s"."%s" = "%s"."%s")' % (lhs, lhs_col, alias, col) self.where_clause.append(condition) else: @@ -123,13 +135,13 @@ class Query(object): return alias, alias_statement else: aliases = self._get_table_aliases() - assert lhs in aliases, "Left-hand-side table %s must already be part of the query tables %s!" % (lhs, str(self.tables)) - if alias_statement in self.tables: + assert lhs in aliases, "Left-hand-side table %s must already be part of the query tables %s!" % (lhs, str(self._tables)) + if alias_statement in self._tables: # already joined, must ignore (promotion to outer and multiple joins not supported yet) pass else: # add JOIN - self.tables.append(alias_statement) + self._tables.append(alias_statement) join_tuple = (alias, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN') self.joins.setdefault(lhs, []).append(join_tuple) if extra or extra_params: @@ -140,7 +152,7 @@ class Query(object): def get_sql(self): """ Returns (query_from, query_where, query_params). """ from odoo.osv.expression import get_alias_from_query - tables_to_process = list(self.tables) + tables_to_process = list(self._tables) alias_mapping = self._get_alias_mapping() from_clause = [] from_params = [] |
