From ac587f68fec384730e4fbb748cd3662659037660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 28 Nov 2012 10:50:42 +0100 Subject: [PATCH] [IMP] expression.py: added support of _auto_join in parse. The purpose is to add join clauses to queries instead of replacing many2one or one2many fields by id in ... equivalent leafs. This should lessen the number of executed queries, as well as the time required for some queries like mailboxes queries. Updated the generation of full table aliases when generating SQL, to avoid having a table having it own name as alias. bzr revid: tde@openerp.com-20121128095042-x5rzgwydv0g9uy0m --- openerp/osv/expression.py | 47 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 947d392065e..4f2ae3989b9 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -403,7 +403,11 @@ class expression(object): def _get_full_alias(self, alias): if not self._get_table_from_alias(alias): return False - return '%s as %s' % (self._get_table_from_alias(alias)._table, alias) + table_name = self._get_table_from_alias(alias)._table + if table_name == alias: + return '"%s"' % alias + else: + return '"%s" as %s' % (table_name, alias) def _add_table_alias(self, alias, table): if not self._has_table_alias(alias): @@ -414,7 +418,7 @@ class expression(object): def get_tables(self): """ Returns the list of tables for SQL queries, like select from ... """ - return ['"%s"' % item for item in self.table_aliases] + return [self._get_full_alias(item) for item in self.table_aliases] def parse(self, cr, uid, exp, table, context): """ Transform the leaves of the expression @@ -540,6 +544,7 @@ class expression(object): # - get relational table that exists for relational fields # - prepare the alias for tables, that can be the table name if only one-level # - if domain is a path (ex: ('partner_id.name', '=', 'foo')): + # TDE TO ADD: once fully implemented, explain auto_join # replace all the expression by a normalized equivalent domain # - find the related ids: partner_id.name='foo' -> res_partner.search(('name', '=', 'foo'))) # - many2one: leaf becomes directly ('partner_id', 'in', 'partner_ids') @@ -555,6 +560,44 @@ class expression(object): relational_table = table.pool.get(field._obj) alias = working_table._table + while len(field_path) > 1 and field._auto_join: + if not field._type in ['many2one', 'one2many']: + raise '_auto_join attribute on something else than a many2one or one2many is currently not implemented... crashing' + + previous_alias = alias + alias = alias + '__' + field_path[0] + if not self._get_table_from_alias(alias): + self._add_table_alias(alias, relational_table) + if field._type == 'many2one': + self.joins.append('%s."%s"=%s."%s"' % (alias, 'id', previous_alias, field_path[0])) + elif field._type == 'one2many': + self.joins.append('%s."%s"=%s."%s"' % (alias, field._fields_id, previous_alias, 'id')) + self.exp[i] = (alias + '.' + field_path[1], self.exp[i][1], self.exp[i][2]) + + # udpate working variables + field_path = field_path[1].split('.', 1) + working_table = relational_table + field = working_table._columns.get(field_path[0]) + + if not field: + if left == 'id' and operator == 'child_of': + ids2 = to_ids(right, table) + dom = child_of_domain(left, ids2, working_table) + self.exp = self.exp[:i] + dom + self.exp[i + 1:] + else: + # field could not be found in model columns, it's probably invalid, unless + # it's one of the _log_access special fields + # TODO: make these fields explicitly available in self.columns instead! + if field_path[0] not in MAGIC_COLUMNS: + raise ValueError("Invalid field %r in domain expression %r" % (left, exp)) + break + + # moved on top + relational_table = table.pool.get(field._obj) + + if not field: + continue + if len(field_path) > 1: if field._type == 'many2one': right = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context)