Merge lp:~openerp-dev/openobject-server/7.0-unaccentwrapper-chs into lp:openobject-server/7.0
- 7.0-unaccentwrapper-chs
- Merge into 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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenERP Core Team | Pending | ||
Review via email: mp+214254@code.launchpad.net |
Commit message
Description of the change
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 |