[IMP] core: better tests on queries made by search

This commit is contained in:
Raphael Collet
2020-05-20 07:56:14 +00:00
parent e3be55d3f6
commit 020536142c
3 changed files with 621 additions and 206 deletions
+580 -196
View File
@@ -777,27 +777,7 @@ class TestExpression2(TransactionCase):
class TestAutoJoin(TransactionCase):
def setUp(self):
super(TestAutoJoin, self).setUp()
# Mock BaseModel._where_calc(), to be able to proceed to some tests about generated expression
self._reinit_mock()
BaseModel_where_calc = BaseModel._where_calc
def _where_calc(model, *args, **kwargs):
""" Mock `_where_calc` to be able to test its results. Store them
into some internal variable for latter processing. """
query = BaseModel_where_calc(model, *args, **kwargs)
self.query_list.append(query)
return query
self.patch(BaseModel, '_where_calc', _where_calc)
def _reinit_mock(self):
self.query_list = []
def test_auto_join(self):
unaccent = expression.get_unaccent_wrapper(self.cr)
# Get models
partner_obj = self.env['res.partner']
state_obj = self.env['res.country.state']
@@ -819,6 +799,7 @@ class TestAutoJoin(TransactionCase):
# Create demo data: partners and bank object
p_a = partner_obj.create({'name': 'test__A', 'state_id': states[0].id})
p_b = partner_obj.create({'name': 'test__B', 'state_id': states[1].id})
p_c = partner_obj.create({'name': 'test__C', 'state_id': False})
p_aa = partner_obj.create({'name': 'test__AA', 'parent_id': p_a.id, 'state_id': states[0].id})
p_ab = partner_obj.create({'name': 'test__AB', 'parent_id': p_a.id, 'state_id': states[1].id})
p_ba = partner_obj.create({'name': 'test__BA', 'parent_id': p_b.id, 'state_id': states[0].id})
@@ -841,111 +822,46 @@ class TestAutoJoin(TransactionCase):
name_test = '12'
# Do: one2many without _auto_join
self._reinit_mock()
partners = partner_obj.search([('bank_ids.sanitized_acc_number', 'like', name_test)])
# Test result
self.assertEqual(partners, p_aa,
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 2,
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') should produce 2 queries (1 in res_partner_bank, 1 on res_partner)")
sql_query = self.query_list[0].get_sql()
self.assertIn('res_partner_bank', sql_query[0],
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect main table")
expected = "%s like %s" % (unaccent('"res_partner_bank"."sanitized_acc_number"::text'), unaccent('%s'))
self.assertIn(expected, sql_query[1],
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect where condition")
self.assertEqual(['%' + name_test + '%'], sql_query[2],
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect parameter")
sql_query = self.query_list[1].get_sql()
self.assertIn('res_partner', sql_query[0],
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') second query incorrect main table")
self.assertIn('"res_partner"."id" in (%s)', sql_query[1],
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') second query incorrect where condition")
self.assertIn(p_aa.id, sql_query[2],
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') second query incorrect parameter")
partners = partner_obj.search(['|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', name_test)])
self.assertIn(p_aa, partners,
"_auto_join off: '|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result")
self.assertIn(p_c, partners,
"_auto_join off: '|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result")
# Do: cascaded one2many without _auto_join
self._reinit_mock()
partners = partner_obj.search([('child_ids.bank_ids.id', 'in', [b_aa.id, b_ba.id])])
# Test result
self.assertEqual(partners, p_a + p_b,
"_auto_join off: ('child_ids.bank_ids.id', 'in', [..]): incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 3,
"_auto_join off: ('child_ids.bank_ids.id', 'in', [..]) should produce 3 queries (1 in res_partner_bank, 2 on res_partner)")
# Do: one2many with _auto_join
patch_auto_join(partner_obj, 'bank_ids', True)
self._reinit_mock()
partners = partner_obj.search([('bank_ids.sanitized_acc_number', 'like', name_test)])
# Test result
self.assertEqual(partners, p_aa,
"_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect main table")
self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0],
"_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect join")
expected = "%s like %s" % (unaccent('"res_partner__bank_ids"."sanitized_acc_number"::text'), unaccent('%s'))
self.assertIn(expected, sql_query[1],
"_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect where condition")
self.assertIn('"res_partner"."id"="res_partner__bank_ids"."partner_id"', sql_query[1],
"_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect join condition")
self.assertIn('%' + name_test + '%', sql_query[2],
"_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect parameter")
partners = partner_obj.search(['|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', name_test)])
self.assertIn(p_aa, partners,
"_auto_join on: '|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result")
# TO FIX: this is not correct
self.assertNotIn(p_c, partners,
"_auto_join on: '|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result")
# Do: one2many with _auto_join, test final leaf is an id
self._reinit_mock()
bank_ids = [b_aa.id, b_ab.id]
partners = partner_obj.search([('bank_ids.id', 'in', bank_ids)])
# Test result
self.assertEqual(partners, p_aa + p_ab,
"_auto_join on: ('bank_ids.id', 'in', [..]) incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('bank_ids.id', 'in', [..]) should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect main table")
self.assertIn('"res_partner__bank_ids"."id" in (%s,%s)', sql_query[1],
"_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect where condition")
self.assertLessEqual(set(bank_ids), set(sql_query[2]),
"_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect parameter")
# Do: 2 cascaded one2many with _auto_join, test final leaf is an id
patch_auto_join(partner_obj, 'child_ids', True)
self._reinit_mock()
bank_ids = [b_aa.id, b_ba.id]
partners = partner_obj.search([('child_ids.bank_ids.id', 'in', bank_ids)])
# Test result
self.assertEqual(partners, p_a + p_b,
"_auto_join on: ('child_ids.bank_ids.id', 'not in', [..]): incorrect result")
# # Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) incorrect main table")
self.assertIn('"res_partner" as "res_partner__child_ids"', sql_query[0],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join")
self.assertIn('"res_partner_bank" as "res_partner__child_ids__bank_ids"', sql_query[0],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join")
self.assertIn('"res_partner__child_ids__bank_ids"."id" in (%s,%s)', sql_query[1],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect where condition")
self.assertIn('"res_partner"."id"="res_partner__child_ids"."parent_id"', sql_query[1],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition")
self.assertIn('"res_partner__child_ids"."id"="res_partner__child_ids__bank_ids"."partner_id"', sql_query[1],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition")
self.assertLessEqual(set(bank_ids), set(sql_query[2][-2:]),
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect parameter")
# --------------------------------------------------
# Test3: many2one
@@ -953,107 +869,38 @@ class TestAutoJoin(TransactionCase):
name_test = 'US'
# Do: many2one without _auto_join
self._reinit_mock()
partners = partner_obj.search([('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b + p_aa + p_ab + p_ba, partners,
"_auto_join off: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 3,
"_auto_join off: ('state_id.country_id.code', 'like', '..') should produce 3 queries (1 on res_country, 1 on res_country_state, 1 on res_partner)")
partners = partner_obj.search(['|', ('state_id.code', '=', states[0].code), ('name', 'like', 'C')])
self.assertIn(p_a, partners, '_auto_join off: disjunction incorrect result')
self.assertIn(p_c, partners, '_auto_join off: disjunction incorrect result')
# Do: many2one with 1 _auto_join on the first many2one
patch_auto_join(partner_obj, 'state_id', True)
self._reinit_mock()
partners = partner_obj.search([('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b + p_aa + p_ab + p_ba, partners,
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 2,
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') should produce 2 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_country"', sql_query[0],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
expected = "%s like %s" % (unaccent('"res_country"."code"::text'), unaccent('%s'))
self.assertIn(expected, sql_query[1],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
self.assertEqual(['%' + name_test + '%'], sql_query[2],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter")
sql_query = self.query_list[1].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table")
self.assertIn('"res_country_state" as "res_partner__state_id"', sql_query[0],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join")
self.assertIn('"res_partner__state_id"."country_id" in (%s)', sql_query[1],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition")
self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join condition")
partners = partner_obj.search(['|', ('state_id.code', '=', states[0].code), ('name', 'like', 'C')])
self.assertIn(p_a, partners, '_auto_join: disjunction incorrect result')
# TO FIX: this is not correct
self.assertNotIn(p_c, partners, '_auto_join: disjunction incorrect result')
# Do: many2one with 1 _auto_join on the second many2one
patch_auto_join(partner_obj, 'state_id', False)
patch_auto_join(state_obj, 'country_id', True)
self._reinit_mock()
partners = partner_obj.search([('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b + p_aa + p_ab + p_ba, partners,
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 2,
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') should produce 2 query")
# -- first query
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_country_state"', sql_query[0],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
self.assertIn('"res_country" as "res_country_state__country_id"', sql_query[0],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join")
expected = "%s like %s" % (unaccent('"res_country_state__country_id"."code"::text'), unaccent('%s'))
self.assertIn(expected, sql_query[1],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
self.assertIn('"res_country_state"."country_id"="res_country_state__country_id"."id"', sql_query[1],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join condition")
self.assertEqual(['%' + name_test + '%'], sql_query[2],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter")
# -- second query
sql_query = self.query_list[1].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table")
self.assertIn('"res_partner"."state_id" in', sql_query[1],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition")
# Do: many2one with 2 _auto_join
patch_auto_join(partner_obj, 'state_id', True)
patch_auto_join(state_obj, 'country_id', True)
self._reinit_mock()
partners = partner_obj.search([('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b + p_aa + p_ab + p_ba, partners,
"_auto_join on: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('state_id.country_id.code', 'like', '..') should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect main table")
self.assertIn('"res_country_state" as "res_partner__state_id"', sql_query[0],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
self.assertIn('"res_country" as "res_partner__state_id__country_id"', sql_query[0],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
expected = "%s like %s" % (unaccent('"res_partner__state_id__country_id"."code"::text'), unaccent('%s'))
self.assertIn(expected, sql_query[1],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition")
self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition")
self.assertIn('"res_partner__state_id"."country_id"="res_partner__state_id__country_id"."id"', sql_query[1],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition")
self.assertIn('%' + name_test + '%', sql_query[2],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect parameter")
# --------------------------------------------------
# Test4: domain attribute on one2many fields
@@ -1063,28 +910,16 @@ class TestAutoJoin(TransactionCase):
patch_auto_join(partner_obj, 'bank_ids', True)
patch_domain(partner_obj, 'child_ids', lambda self: ['!', ('name', '=', self._name)])
patch_domain(partner_obj, 'bank_ids', [('sanitized_acc_number', 'like', '2')])
# Do: 2 cascaded one2many with _auto_join, test final leaf is an id
self._reinit_mock()
partners = partner_obj.search(['&', (1, '=', 1), ('child_ids.bank_ids.id', 'in', [b_aa.id, b_ba.id])])
# Test result: at least one of our added data
self.assertLessEqual(p_a, partners,
"_auto_join on one2many with domains incorrect result")
self.assertFalse((p_ab + p_ba) & partners,
"_auto_join on one2many with domains incorrect result")
# Test produced queries that domains effectively present
sql_query = self.query_list[0].get_sql()
expected = "%s like %s" % (unaccent('"res_partner__child_ids__bank_ids"."sanitized_acc_number"::text'), unaccent('%s'))
self.assertIn(expected, sql_query[1],
"_auto_join on one2many with domains incorrect result")
# TDE TODO: check first domain has a correct table name
self.assertIn('"res_partner__child_ids"."name" = %s', sql_query[1],
"_auto_join on one2many with domains incorrect result")
patch_domain(partner_obj, 'child_ids', lambda self: [('name', '=', '__%s' % self._name)])
self._reinit_mock()
partners = partner_obj.search(['&', (1, '=', 1), ('child_ids.bank_ids.id', 'in', [b_aa.id, b_ba.id])])
# Test result: no one
self.assertFalse(partners,
"_auto_join on one2many with domains incorrect result")
@@ -1101,27 +936,17 @@ class TestAutoJoin(TransactionCase):
patch_domain(partner_obj, 'bank_ids', [])
# Do: ('child_ids.state_id.country_id.code', 'like', '..') without _auto_join
self._reinit_mock()
partners = partner_obj.search([('child_ids.state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b, partners,
"_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 4,
"_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect")
# Do: ('child_ids.state_id.country_id.code', 'like', '..') with _auto_join
patch_auto_join(partner_obj, 'child_ids', True)
patch_auto_join(partner_obj, 'state_id', True)
patch_auto_join(state_obj, 'country_id', True)
self._reinit_mock()
partners = partner_obj.search([('child_ids.state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b, partners,
"_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect")
def test_nullfields(self):
obj1 = self.env['res.bank'].create({'name': 'c0'})
@@ -1146,3 +971,562 @@ class TestAutoJoin(TransactionCase):
(obj1 | obj2 | obj3),
"Should have returned all banks whose city doesn't contain field"
)
class TestQueries(TransactionCase):
def test_logic(self):
Model = self.env['res.partner']
domain = [
'&', ('name', 'like', 'foo'),
'|', ('title', '=', 1), '!', ('ref', '=', '42'),
]
Model.search(domain)
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner"
WHERE (("res_partner"."active" = %s) AND (
("res_partner"."name"::text LIKE %s) AND (
("res_partner"."title" = %s) OR (
("res_partner"."ref" != %s) OR
"res_partner"."ref" IS NULL
)
)
))
ORDER BY "res_partner"."display_name"
''']):
Model.search(domain)
def test_translated_field(self):
self.env['res.lang']._activate_lang('fr_FR')
Model = self.env['res.partner.title'].with_context(lang='fr_FR')
Model.search([('name', 'ilike', 'foo')])
with self.assertQueries(['''
SELECT "res_partner_title".id
FROM "res_partner_title"
LEFT JOIN "ir_translation" AS "res_partner_title__name" ON
("res_partner_title"."id" = "res_partner_title__name"."res_id"
AND "res_partner_title__name"."type" = 'model'
AND "res_partner_title__name"."name" = %s
AND "res_partner_title__name"."lang" = %s
AND "res_partner_title__name"."value" != %s)
WHERE ("res_partner_title"."id" IN (
WITH temp_irt_current (id, name) AS (
SELECT ct.id, coalesce(it.value, ct."name")
FROM res_partner_title ct
LEFT JOIN ir_translation it ON
(it.name = %s AND it.lang = %s AND it.type = %s
AND it.res_id = ct.id AND it.value != '')
)
SELECT id
FROM temp_irt_current
WHERE name LIKE %s
ORDER BY name
))
ORDER BY COALESCE("res_partner_title__name"."value", "res_partner_title"."name")
''']):
Model.search([('name', 'like', 'foo')])
with self.assertQueries(['''
SELECT COUNT(1)
FROM "res_partner_title"
LEFT JOIN "ir_translation" AS "res_partner_title__name" ON
("res_partner_title"."id" = "res_partner_title__name"."res_id"
AND "res_partner_title__name"."type" = 'model'
AND "res_partner_title__name"."name" = %s
AND "res_partner_title__name"."lang" = %s
AND "res_partner_title__name"."value" != %s)
WHERE ("res_partner_title"."id" = %s)
''']):
Model.search_count([('id', '=', 1)])
@mute_logger('odoo.models.unlink')
def test_access_rules(self):
Model = self.env['res.users'].with_user(self.env.ref('base.user_admin'))
self.env['ir.rule'].search([]).unlink()
self.env['ir.rule'].create([{
'name': 'users rule',
'model_id': self.env['ir.model']._get('res.users').id,
'domain_force': str([('id', '=', 1)]),
}, {
'name': 'partners rule',
'model_id': self.env['ir.model']._get('res.partner').id,
'domain_force': str([('id', '=', 1)]),
}])
Model.search([])
with self.assertQueries(['''
SELECT "res_users".id
FROM "res_users", "res_partner" AS "res_users__partner_id"
WHERE ("res_users"."active" = %s)
AND ("res_users"."id" = %s)
AND ("res_users"."partner_id" = "res_users__partner_id"."id")
AND ("res_users__partner_id"."id" = %s)
ORDER BY "res_users__partner_id"."name", "res_users"."login"
''']):
Model.search([])
class TestMany2one(TransactionCase):
def setUp(self):
super().setUp()
self.Partner = self.env['res.partner'].with_context(active_test=False)
self.User = self.env['res.users'].with_context(active_test=False)
self.company = self.env['res.company'].browse(1)
def test_inherited(self):
with self.assertQueries(['''
SELECT "res_users".id
FROM "res_users", "res_partner" AS "res_users__partner_id"
WHERE ("res_users"."partner_id" = "res_users__partner_id"."id")
AND ("res_users__partner_id"."name"::text LIKE %s)
ORDER BY "res_users__partner_id"."name", "res_users"."login"
''']):
self.User.search([('name', 'like', 'foo')])
def test_regular(self):
self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
self.Partner.search([('country_id.code', 'like', 'BE')])
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."company_id" = %s)
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('company_id', '=', self.company.id)])
with self.assertQueries(['''
SELECT "res_company".id
FROM "res_company"
WHERE ("res_company"."name"::text LIKE %s)
ORDER BY "res_company"."sequence", "res_company"."name"
''', '''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."company_id" IN (%s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('company_id.name', 'like', self.company.name)])
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."name"::text LIKE %s)
ORDER BY "res_partner"."display_name"
''', '''
SELECT "res_company".id
FROM "res_company"
WHERE ("res_company"."partner_id" IN (%s))
ORDER BY "res_company"."sequence", "res_company"."name"
''', '''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."company_id" IN (%s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
with self.assertQueries(['''
SELECT "res_company".id
FROM "res_company"
WHERE ("res_company"."name"::text LIKE %s)
ORDER BY "res_company"."sequence", "res_company"."name"
''', '''
SELECT "res_country".id
FROM "res_country"
WHERE ("res_country"."code"::text LIKE %s)
ORDER BY "res_country"."name"
''', '''
SELECT "res_partner".id
FROM "res_partner"
WHERE (("res_partner"."company_id" IN (%s)) OR ("res_partner"."country_id" IN (%s)))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([
'|',
('company_id.name', 'like', self.company.name),
('country_id.code', 'like', 'BE'),
])
def test_autojoin(self):
# auto_join on the first many2one
self.patch(self.Partner._fields['company_id'], 'auto_join', True)
self.patch(self.company._fields['partner_id'], 'auto_join', False)
self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner", "res_company" AS "res_partner__company_id"
WHERE ("res_partner"."company_id" = "res_partner__company_id"."id")
AND ("res_partner__company_id"."name"::text LIKE %s)
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('company_id.name', 'like', self.company.name)])
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."name"::text LIKE %s)
ORDER BY "res_partner"."display_name"
''', '''
SELECT "res_partner".id
FROM "res_partner", "res_company" AS "res_partner__company_id"
WHERE ("res_partner"."company_id" = "res_partner__company_id"."id")
AND ("res_partner__company_id"."partner_id" IN (%s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
# auto_join on the second many2one
self.patch(self.Partner._fields['company_id'], 'auto_join', False)
self.patch(self.company._fields['partner_id'], 'auto_join', True)
self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
with self.assertQueries(['''
SELECT "res_company".id
FROM "res_company", "res_partner" AS "res_company__partner_id"
WHERE ("res_company"."partner_id" = "res_company__partner_id"."id")
AND ("res_company__partner_id"."name"::text LIKE %s)
ORDER BY "res_company"."sequence", "res_company"."name"
''', '''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."company_id" IN (%s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
# auto_join on both many2one
self.patch(self.Partner._fields['company_id'], 'auto_join', True)
self.patch(self.company._fields['partner_id'], 'auto_join', True)
self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner", "res_company" AS "res_partner__company_id",
"res_partner" AS "res_partner__company_id__partner_id"
WHERE ("res_partner"."company_id" = "res_partner__company_id"."id"
AND "res_partner__company_id"."partner_id" = "res_partner__company_id__partner_id"."id")
AND ("res_partner__company_id__partner_id"."name"::text LIKE %s)
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
# union with two auto_join
self.patch(self.Partner._fields['company_id'], 'auto_join', True)
self.patch(self.Partner._fields['country_id'], 'auto_join', True)
self.Partner.search([
'|',
('company_id.name', 'like', self.company.name),
('country_id.code', 'like', 'BE'),
])
# WRONG QUERY: it discards results where company_id or country_id are NULL
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner", "res_company" AS "res_partner__company_id",
"res_country" AS "res_partner__country_id"
WHERE ("res_partner"."company_id" = "res_partner__company_id"."id"
AND "res_partner"."country_id" = "res_partner__country_id"."id")
AND (("res_partner__company_id"."name"::text LIKE %s)
OR ("res_partner__country_id"."code"::text LIKE %s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([
'|',
('company_id.name', 'like', self.company.name),
('country_id.code', 'like', 'BE'),
])
def test_name_search(self):
self.Partner.search([('company_id', 'like', self.company.name)])
with self.assertQueries(['''
SELECT "res_company".id
FROM "res_company"
WHERE ("res_company"."name"::text LIKE %s)
ORDER BY "res_company"."sequence", "res_company"."name"
''', '''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."company_id" IN (%s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('company_id', 'like', self.company.name)])
class TestOne2many(TransactionCase):
def setUp(self):
super().setUp()
self.Partner = self.env['res.partner'].with_context(active_test=False)
self.partner = self.Partner.create({
'name': 'Foo',
'bank_ids': [
(0, 0, {'acc_number': '123', 'acc_type': 'bank'}),
(0, 0, {'acc_number': '456', 'acc_type': 'bank'}),
(0, 0, {'acc_number': '789', 'acc_type': 'bank'}),
],
})
def test_regular(self):
self.Partner.search([('bank_ids', 'in', self.partner.bank_ids.ids)])
self.Partner.search([('bank_ids.sanitized_acc_number', 'like', '12')])
self.Partner.search([('child_ids.bank_ids.sanitized_acc_number', 'like', '12')])
with self.assertQueries(['''
SELECT DISTINCT "partner_id"
FROM "res_partner_bank"
WHERE "id" IN %s
''', '''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."id" IN (%s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('bank_ids', 'in', self.partner.bank_ids.ids)])
with self.assertQueries(['''
SELECT "res_partner_bank".id
FROM "res_partner_bank"
WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s)
ORDER BY "res_partner_bank"."sequence", "res_partner_bank"."id"
''', '''
SELECT DISTINCT "partner_id"
FROM "res_partner_bank"
WHERE "id" IN %s
''', '''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."id" IN (%s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('bank_ids.sanitized_acc_number', 'like', '12')])
with self.assertQueries(['''
SELECT "res_partner_bank".id
FROM "res_partner_bank"
WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s)
ORDER BY "res_partner_bank"."sequence", "res_partner_bank"."id"
''', '''
SELECT DISTINCT "partner_id"
FROM "res_partner_bank"
WHERE "id" IN %s
''', '''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."id" IN (%s))
ORDER BY "res_partner"."display_name"
''', '''
SELECT DISTINCT "parent_id"
FROM "res_partner"
WHERE "id" IN %s
''', '''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."id" IN (%s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('child_ids.bank_ids.sanitized_acc_number', 'like', '12')])
def test_autojoin(self):
self.patch(self.Partner._fields['bank_ids'], 'auto_join', True)
self.patch(self.Partner._fields['child_ids'], 'auto_join', True)
self.Partner.search([('bank_ids', 'in', self.partner.bank_ids.ids)])
self.Partner.search([('bank_ids.sanitized_acc_number', 'like', '12')])
self.Partner.search([('child_ids.bank_ids.sanitized_acc_number', 'like', '12')])
with self.assertQueries(['''
SELECT DISTINCT "partner_id"
FROM "res_partner_bank"
WHERE "id" IN %s
''', '''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."id" IN (%s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('bank_ids', 'in', self.partner.bank_ids.ids)])
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner", "res_partner_bank" AS "res_partner__bank_ids"
WHERE ("res_partner"."id" = "res_partner__bank_ids"."partner_id")
AND ("res_partner__bank_ids"."sanitized_acc_number"::text LIKE %s)
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('bank_ids.sanitized_acc_number', 'like', '12')])
# WRONG QUERY: the domain semantics is having some line matching '12'
# and having some line matching '45', but not necessarily the same line!
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner", "res_partner_bank" AS "res_partner__bank_ids"
WHERE ("res_partner"."id" = "res_partner__bank_ids"."partner_id")
AND (("res_partner__bank_ids"."sanitized_acc_number"::text LIKE %s)
AND ("res_partner__bank_ids"."sanitized_acc_number"::text LIKE %s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([
('bank_ids.sanitized_acc_number', 'like', '12'),
('bank_ids.sanitized_acc_number', 'like', '45'),
])
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner", "res_partner" AS "res_partner__child_ids",
"res_partner_bank" AS "res_partner__child_ids__bank_ids"
WHERE ("res_partner"."id" = "res_partner__child_ids"."parent_id"
AND "res_partner__child_ids"."id" = "res_partner__child_ids__bank_ids"."partner_id")
AND (("res_partner__child_ids"."active" = %s)
AND ("res_partner__child_ids__bank_ids"."sanitized_acc_number"::text LIKE %s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('child_ids.bank_ids.sanitized_acc_number', 'like', '12')])
# check domains on one2many fields
self.patch(self.Partner._fields['bank_ids'], 'domain',
[('sanitized_acc_number', 'like', '2')])
self.patch(self.Partner._fields['child_ids'], 'domain',
lambda self: ['!', ('name', '=', self._name)])
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner", "res_partner" AS "res_partner__child_ids",
"res_partner_bank" AS "res_partner__child_ids__bank_ids"
WHERE ("res_partner"."id" = "res_partner__child_ids"."parent_id"
AND "res_partner__child_ids"."id" = "res_partner__child_ids__bank_ids"."partner_id")
AND ((NOT (("res_partner__child_ids"."name" = %s)))
AND (("res_partner__child_ids__bank_ids"."sanitized_acc_number"::text LIKE %s)
AND ("res_partner__child_ids__bank_ids"."id" in (%s,%s,%s))))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('child_ids.bank_ids.id', 'in', self.partner.bank_ids.ids)])
def test_autojoin_mixed(self):
self.patch(self.Partner._fields['child_ids'], 'auto_join', True)
self.patch(self.Partner._fields['state_id'], 'auto_join', True)
self.patch(self.Partner.state_id._fields['country_id'], 'auto_join', True)
self.Partner.search([('child_ids.state_id.country_id.code', 'like', 'US')])
with self.assertQueries(['''
SELECT "res_partner".id
FROM "res_partner", "res_partner" AS "res_partner__child_ids",
"res_country_state" AS "res_partner__child_ids__state_id",
"res_country" AS "res_partner__child_ids__state_id__country_id"
WHERE ("res_partner"."id" = "res_partner__child_ids"."parent_id"
AND "res_partner__child_ids"."state_id" = "res_partner__child_ids__state_id"."id"
AND "res_partner__child_ids__state_id"."country_id" = "res_partner__child_ids__state_id__country_id"."id")
AND (("res_partner__child_ids"."active" = %s)
AND ("res_partner__child_ids__state_id__country_id"."code"::text LIKE %s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('child_ids.state_id.country_id.code', 'like', 'US')])
def test_name_search(self):
self.Partner.search([('bank_ids', 'like', '12')])
with self.assertQueries(['''
SELECT "res_partner_bank".id
FROM "res_partner_bank"
WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s)
ORDER BY "res_partner_bank"."sequence", "res_partner_bank"."id"
''', '''
SELECT DISTINCT "partner_id"
FROM "res_partner_bank"
WHERE "id" IN %s
''', '''
SELECT "res_partner".id
FROM "res_partner"
WHERE ("res_partner"."id" IN (%s))
ORDER BY "res_partner"."display_name"
''']):
self.Partner.search([('bank_ids', 'like', '12')])
class TestMany2many(TransactionCase):
def setUp(self):
super().setUp()
self.User = self.env['res.users'].with_context(active_test=False)
self.company = self.env['res.company'].browse(1)
def test_regular(self):
group = self.env.ref('base.group_user')
rule = group.rule_groups[0]
self.User.search([('groups_id', 'in', group.ids)], order='id')
self.User.search([('groups_id.name', 'like', group.name)], order='id')
self.User.search([('groups_id.rule_groups.name', 'like', rule.name)], order='id')
with self.assertQueries(['''
SELECT "res_users".id
FROM "res_users"
WHERE ("res_users"."id" IN (
SELECT "uid" FROM "res_groups_users_rel" WHERE "gid" IN %s
))
ORDER BY "res_users"."id"
''']):
self.User.search([('groups_id', 'in', group.ids)], order='id')
with self.assertQueries(['''
SELECT "res_groups".id
FROM "res_groups"
WHERE ("res_groups"."color" = %s)
ORDER BY "res_groups"."name"
''', '''
SELECT "res_users".id
FROM "res_users"
WHERE ("res_users"."id" IN (
SELECT "uid" FROM "res_groups_users_rel" WHERE "gid" IN %s
))
ORDER BY "res_users"."id"
''']):
self.User.search([('groups_id.color', '=', group.color)], order='id')
with self.assertQueries(['''
SELECT "ir_rule".id
FROM "ir_rule"
LEFT JOIN "ir_model" AS "ir_rule__model_id" ON ("ir_rule"."model_id" = "ir_rule__model_id"."id")
WHERE ("ir_rule"."name"::text LIKE %s)
ORDER BY "ir_rule__model_id"."model" DESC, "ir_rule"."id"
''', '''
SELECT "res_groups".id
FROM "res_groups"
WHERE ("res_groups"."id" IN (
SELECT "group_id" FROM "rule_group_rel" WHERE "rule_group_id" IN %s
))
ORDER BY "res_groups"."name"
''', '''
SELECT "res_users".id
FROM "res_users"
WHERE ("res_users"."id" IN (
SELECT "uid" FROM "res_groups_users_rel" WHERE "gid" IN %s
))
ORDER BY "res_users"."id"
''']):
self.User.search([('groups_id.rule_groups.name', 'like', rule.name)], order='id')
def test_autojoin(self):
self.patch(self.User._fields['groups_id'], 'auto_join', True)
with self.assertRaises(NotImplementedError):
self.User.search([('groups_id.name', '=', 'foo')])
def test_name_search(self):
self.User.search([('company_ids', 'like', self.company.name)], order='id')
with self.assertQueries(['''
SELECT "res_company".id
FROM "res_company"
WHERE ("res_company"."name"::text LIKE %s)
ORDER BY "res_company"."sequence", "res_company"."name"
''', '''
SELECT "res_users".id
FROM "res_users"
WHERE ("res_users"."id" IN (
SELECT "user_id" FROM "res_company_users_rel" WHERE "cid" IN %s
))
ORDER BY "res_users"."id"
''']):
self.User.search([('company_ids', 'like', self.company.name)], order='id')
+9 -10
View File
@@ -583,12 +583,13 @@ class ExtendedLeaf(object):
return conditions
def get_tables(self):
tables = set()
tables = []
links = []
for context in self.join_context:
links.append((context[1]._table, context[4]))
alias, alias_statement = generate_table_alias(self._models[0]._table, links)
tables.add(alias_statement)
if alias_statement not in tables:
tables.append(alias_statement)
return tables
def _get_context_debug(self):
@@ -676,14 +677,11 @@ class expression(object):
def get_tables(self):
""" Returns the list of tables for SQL queries, like select from ... """
tables = []
tables = [_quote(self.root_model._table)]
for leaf in self.result:
for table in leaf.get_tables():
if table not in tables:
tables.append(table)
table_name = _quote(self.root_model._table)
if table_name not in tables:
tables.append(table_name)
return tables
# ----------------------------------------
@@ -1158,10 +1156,11 @@ class expression(object):
# -> generate joins
# ----------------------------------------
joins = set()
for leaf in self.result:
joins |= set(leaf.get_join_conditions())
self.joins = list(joins)
self.joins = list(tools.unique(
join
for leaf in self.result
for join in leaf.get_join_conditions()
))
def __leaf_to_sql(self, eleaf):
model = eleaf.model
+32
View File
@@ -40,6 +40,7 @@ from xmlrpc import client as xmlrpclib
from odoo.models import BaseModel
from odoo.osv.expression import normalize_domain, TRUE_LEAF, FALSE_LEAF
from odoo.sql_db import Cursor
from odoo.tools import float_compare, single_email_re
from odoo.tools.misc import find_in_path
from odoo.tools.safe_eval import safe_eval
@@ -379,6 +380,37 @@ class BaseCase(TreeCase, MetaCase('DummyCase', (object,), {})):
else:
return self._assertRaises(exception, **kwargs)
@contextmanager
def assertQueries(self, expected):
""" Check the queries made by the current cursor. ``expected`` is a list
of strings representing the expected queries being made. Query strings
are matched against each other, ignoring case and whitespaces.
"""
Cursor_execute = Cursor.execute
actual_queries = []
def execute(self, query, params=None, log_exceptions=None):
actual_queries.append(query)
return Cursor_execute(self, query, params, log_exceptions)
def get_unaccent_wrapper(cr):
return lambda x: x
with patch('odoo.sql_db.Cursor.execute', execute):
with patch('odoo.osv.expression.get_unaccent_wrapper', get_unaccent_wrapper):
yield actual_queries
self.assertEqual(
len(actual_queries), len(expected),
"%d queries done, %d expected" % (len(actual_queries), len(expected)),
)
for actual_query, expect_query in zip(actual_queries, expected):
self.assertEqual(
"".join(actual_query.lower().split()),
"".join(expect_query.lower().split()),
"\n---- actual query:\n%s\n---- not like:\n%s" % (actual_query, expect_query),
)
@contextmanager
def assertQueryCount(self, default=0, flush=True, **counters):
""" Context manager that counts queries. It may be invoked either with