Merge lp:~openerp-dev/openobject-server/7.0-unaccentwrapper-chs into lp:openobject-server/7.0

Proposed by Christophe Simonis (OpenERP)
Status: Merged
Merged at revision: 5274
Proposed branch: lp:~openerp-dev/openobject-server/7.0-unaccentwrapper-chs
Merge into: lp:openobject-server/7.0
Diff against target: 237 lines (+64/-39)
3 files modified
openerp/addons/base/res/res_partner.py (+21/-15)
openerp/addons/base/tests/test_expression.py (+26/-6)
openerp/osv/expression.py (+17/-18)
To merge this branch: bzr merge lp:~openerp-dev/openobject-server/7.0-unaccentwrapper-chs
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+214254@code.launchpad.net
To post a comment you must log in.
5273. By Christophe Simonis (OpenERP)

[IMP] expression: new helper for generating unaccent aware sql queries. Adapt _auto_join tests to use it.

5274. By Christophe Simonis (OpenERP)

[FIX] res.partner: name_search: respect unaccent flag

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openerp/addons/base/res/res_partner.py'
2--- openerp/addons/base/res/res_partner.py 2014-04-03 14:37:17 +0000
3+++ openerp/addons/base/res/res_partner.py 2014-04-04 14:16:38 +0000
4@@ -29,6 +29,7 @@
5 from openerp import SUPERUSER_ID
6 from openerp import pooler, tools
7 from openerp.osv import osv, fields
8+from openerp.osv.expression import get_unaccent_wrapper
9 from openerp.tools.translate import _
10 from openerp.tools.yaml_import import is_comment
11
12@@ -610,27 +611,32 @@
13 if operator in ('=ilike', '=like'):
14 operator = operator[1:]
15
16+ unaccent = get_unaccent_wrapper(cr)
17+
18 # TODO: simplify this in trunk with `display_name`, once it is stored
19 # Perf note: a CTE expression (WITH ...) seems to have an even higher cost
20 # than this query with duplicated CASE expressions. The bulk of
21 # the cost is the ORDER BY, and it is inevitable if we want
22 # relevant results for the next step, otherwise we'd return
23 # a random selection of `limit` results.
24- query = ('''SELECT res_partner.id FROM res_partner
25- LEFT JOIN res_partner company
26- ON res_partner.parent_id = company.id'''
27- + where_str + ''' (res_partner.email ''' + operator + ''' %s OR
28- CASE
29- WHEN company.id IS NULL OR res_partner.is_company
30- THEN res_partner.name
31- ELSE company.name || ', ' || res_partner.name
32- END ''' + operator + ''' %s)
33- ORDER BY
34- CASE
35- WHEN company.id IS NULL OR res_partner.is_company
36- THEN res_partner.name
37- ELSE company.name || ', ' || res_partner.name
38- END''')
39+
40+ display_name = """CASE WHEN company.id IS NULL OR res_partner.is_company
41+ THEN {partner_name}
42+ ELSE {company_name} || ', ' || {partner_name}
43+ END""".format(partner_name=unaccent('res_partner.name'),
44+ company_name=unaccent('company.name'))
45+
46+ query = """SELECT res_partner.id
47+ FROM res_partner
48+ LEFT JOIN res_partner company
49+ ON res_partner.parent_id = company.id
50+ {where} ({email} {operator} {percent}
51+ OR {display_name} {operator} {percent})
52+ ORDER BY {display_name}
53+ """.format(where=where_str, operator=operator,
54+ email=unaccent('res_partner.email'),
55+ percent=unaccent('%s'),
56+ display_name=display_name)
57
58 where_clause_params += [search_name, search_name]
59 if limit:
60
61=== modified file 'openerp/addons/base/tests/test_expression.py'
62--- openerp/addons/base/tests/test_expression.py 2014-04-02 11:27:30 +0000
63+++ openerp/addons/base/tests/test_expression.py 2014-04-04 14:16:38 +0000
64@@ -1,4 +1,6 @@
65 import unittest2
66+
67+from openerp.osv.expression import get_unaccent_wrapper
68 from openerp.osv.orm import BaseModel
69 import openerp.tests.common as common
70
71@@ -123,6 +125,7 @@
72
73 def test_20_auto_join(self):
74 registry, cr, uid = self.registry, self.cr, self.uid
75+ unaccent = get_unaccent_wrapper(cr)
76
77 # Get models
78 partner_obj = registry('res.partner')
79@@ -179,8 +182,11 @@
80 sql_query = self.query_list[0].get_sql()
81 self.assertIn('res_partner_bank', sql_query[0],
82 "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect main table")
83- self.assertIn('"res_partner_bank"."name" like %s', sql_query[1],
84+
85+ expected = "%s like %s" % (unaccent('"res_partner_bank"."name"'), unaccent('%s'))
86+ self.assertIn(expected, sql_query[1],
87 "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition")
88+
89 self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]),
90 "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect parameter")
91 sql_query = self.query_list[2].get_sql()
92@@ -216,8 +222,11 @@
93 "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect main table")
94 self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0],
95 "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join")
96- self.assertIn('"res_partner__bank_ids"."name" like %s', sql_query[1],
97+
98+ expected = "%s like %s" % (unaccent('"res_partner__bank_ids"."name"'), unaccent('%s'))
99+ self.assertIn(expected, sql_query[1],
100 "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect where condition")
101+
102 self.assertIn('"res_partner"."id"="res_partner__bank_ids"."partner_id"', sql_query[1],
103 "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join condition")
104 self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]),
105@@ -295,8 +304,11 @@
106 sql_query = self.query_list[0].get_sql()
107 self.assertIn('"res_country"', sql_query[0],
108 "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
109- self.assertIn('"res_country"."code" like %s', sql_query[1],
110+
111+ expected = "%s like %s" % (unaccent('"res_country"."code"'), unaccent('%s'))
112+ self.assertIn(expected, sql_query[1],
113 "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
114+
115 self.assertEqual(['%' + name_test + '%'], sql_query[2],
116 "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter")
117 sql_query = self.query_list[1].get_sql()
118@@ -326,8 +338,11 @@
119 "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
120 self.assertIn('"res_country" as "res_country_state__country_id"', sql_query[0],
121 "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join")
122- self.assertIn('"res_country_state__country_id"."code" like %s', sql_query[1],
123+
124+ expected = "%s like %s" % (unaccent('"res_country_state__country_id"."code"'), unaccent('%s'))
125+ self.assertIn(expected, sql_query[1],
126 "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
127+
128 self.assertIn('"res_country_state"."country_id"="res_country_state__country_id"."id"', sql_query[1],
129 "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join condition")
130 self.assertEqual(['%' + name_test + '%'], sql_query[2],
131@@ -357,8 +372,11 @@
132 "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
133 self.assertIn('"res_country" as "res_partner__state_id__country_id"', sql_query[0],
134 "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
135- self.assertIn('"res_partner__state_id__country_id"."code" like %s', sql_query[1],
136+
137+ expected = "%s like %s" % (unaccent('"res_partner__state_id__country_id"."code"'), unaccent('%s'))
138+ self.assertIn(expected, sql_query[1],
139 "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition")
140+
141 self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1],
142 "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition")
143 self.assertIn('"res_partner__state_id"."country_id"="res_partner__state_id__country_id"."id"', sql_query[1],
144@@ -384,7 +402,9 @@
145 "_auto_join on one2many with domains incorrect result")
146 # Test produced queries that domains effectively present
147 sql_query = self.query_list[0].get_sql()
148- self.assertIn('"res_partner__child_ids__bank_ids"."acc_number" like %s', sql_query[1],
149+
150+ expected = "%s like %s" % (unaccent('"res_partner__child_ids__bank_ids"."acc_number"'), unaccent('%s'))
151+ self.assertIn(expected, sql_query[1],
152 "_auto_join on one2many with domains incorrect result")
153 # TDE TODO: check first domain has a correct table name
154 self.assertIn('"res_partner__child_ids"."name" = %s', sql_query[1],
155
156=== modified file 'openerp/osv/expression.py'
157--- openerp/osv/expression.py 2014-04-03 08:45:24 +0000
158+++ openerp/osv/expression.py 2014-04-04 14:16:38 +0000
159@@ -430,6 +430,10 @@
160 cr.execute('SELECT distinct("%s") FROM "%s" where "%s" is not null' % (select_field, from_table, select_field))
161 return [r[0] for r in cr.fetchall()]
162
163+def get_unaccent_wrapper(cr):
164+ if openerp.modules.registry.RegistryManager.get(cr.dbname).has_unaccent:
165+ return lambda x: "unaccent(%s)" % (x,)
166+ return lambda x: x
167
168 # --------------------------------------------------
169 # ExtendedLeaf class for managing leafs and contexts
170@@ -631,7 +635,7 @@
171 :attr list expression: the domain expression, that will be normalized
172 and prepared
173 """
174- self.has_unaccent = openerp.modules.registry.RegistryManager.get(cr.dbname).has_unaccent
175+ self._unaccent = get_unaccent_wrapper(cr)
176 self.joins = []
177 self.root_model = table
178
179@@ -1020,7 +1024,6 @@
180 push(create_substitution_leaf(leaf, (left, operator, right), working_model))
181
182 elif field.translate and right:
183- field = left
184 need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike')
185 sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator)
186 if need_wildcard:
187@@ -1032,16 +1035,13 @@
188 sql_operator = sql_operator[4:] if sql_operator[:3] == 'not' else '='
189 inselect_operator = 'not inselect'
190
191- trans_left = 'value'
192- left = '"%s"' % (left,)
193- instr = '%s'
194-
195- if self.has_unaccent and sql_operator.endswith('like'):
196- assert isinstance(right, basestring)
197- trans_left = 'unaccent(value)'
198- left = 'unaccent(%s)' % (left,)
199- instr = 'unaccent(%s)'
200- elif sql_operator == 'in':
201+ unaccent = self._unaccent if sql_operator.endswith('like') else lambda x: x
202+
203+ trans_left = unaccent('value')
204+ quote_left = unaccent(_quote(left))
205+ instr = unaccent('%s')
206+
207+ if sql_operator == 'in':
208 # params will be flatten by to_sql() => expand the placeholders
209 instr = '(%s)' % ', '.join(['%s'] * len(right))
210
211@@ -1057,10 +1057,10 @@
212 WHERE {left} {operator} {right}
213 )
214 """.format(trans_left=trans_left, operator=sql_operator,
215- right=instr, table=working_model._table, left=left)
216+ right=instr, table=working_model._table, left=quote_left)
217
218 params = (
219- working_model._name + ',' + field,
220+ working_model._name + ',' + left,
221 context.get('lang') or 'en_US',
222 'model',
223 right,
224@@ -1184,10 +1184,9 @@
225
226 if left in model._columns:
227 format = need_wildcard and '%s' or model._columns[left]._symbol_set[0]
228- if self.has_unaccent and sql_operator.endswith('like'):
229- query = '(unaccent(%s."%s") %s unaccent(%s))' % (table_alias, left, sql_operator, format)
230- else:
231- query = '(%s."%s" %s %s)' % (table_alias, left, sql_operator, format)
232+ unaccent = self._unaccent if sql_operator.endswith('like') else lambda x: x
233+ column = '%s.%s' % (table_alias, _quote(left))
234+ query = '(%s %s %s)' % (unaccent(column), sql_operator, unaccent(format))
235 elif left in MAGIC_COLUMNS:
236 query = "(%s.\"%s\" %s %%s)" % (table_alias, left, sql_operator)
237 params = right