summaryrefslogtreecommitdiff
path: root/odoo/osv
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2020-05-05 12:56:23 +0000
committerRaphael Collet <rco@odoo.com>2020-05-20 07:58:59 +0000
commitdda73afe13f726858876ae4c1b58f1550453d802 (patch)
tree127ac2a24a7671b8f69d54774cf29dc0008e1293 /odoo/osv
parentaed2134b6388258e907f8cb0dbd53857fb32d5c4 (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.py9
-rw-r--r--odoo/osv/query.py30
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 = []