@@ -16,6 +16,15 @@ def get_db_name():
1616 return db
1717
1818
19+ ratios_possible = [
20+ 500 , # 50 %
21+ 50 , # 5 %
22+ 5 , # 0.5 %
23+ 0.5 , # 0.05 %
24+ 0.01 , # 0.001 %
25+ ]
26+
27+
1928class TestPerformanceSQL (BaseCase ):
2029
2130 @classmethod
@@ -31,7 +40,7 @@ def setUpClass(cls):
3140
3241 cls .env = api .Environment (cls .cr , odoo .SUPERUSER_ID , {})
3342
34- def populate (self , nb_tag = 0 , nb_container = 100_000 , nb_line = 500_000 ):
43+ def populate (self , nb_tag = 0 , nb_container = 10_000 , nb_line = 50_000 ):
3544 models = {
3645 'perf.tag' : nb_tag ,
3746 'perf.container' : nb_container ,
@@ -41,23 +50,31 @@ def populate(self, nb_tag=0, nb_container=100_000, nb_line=500_000):
4150 self .env [model ]._custom_populate (nb )
4251
4352 self .env .cr .execute ('ANALYSE;' )
53+ self .env .cr .commit ()
4454
4555 def create_indexes_many2one (self ):
4656 self .env .cr .execute ("CREATE INDEX IF NOT EXISTS perf_line_uniform_container_id ON perf_line(uniform_container_id)" )
4757 self .env .cr .execute ("CREATE INDEX IF NOT EXISTS perf_line_parent_id ON perf_line(parent_id)" )
4858 self .env .cr .commit ()
4959
50- def delete_all_indexes (self ):
60+ def create_indexes_uniform (self ):
61+ for table in ['perf_line' , 'perf_container' , 'perf_tag' ]:
62+ self .env .cr .execute (f"CREATE INDEX IF NOT EXISTS { table } _float_uniform_1000 ON { table } (float_uniform_1000)" )
63+ self .env .cr .commit ()
64+
65+ def delete_indexes_uniform (self ):
5166 for table in ['perf_line' , 'perf_container' , 'perf_tag' ]:
5267 self .env .cr .execute (f"DROP INDEX IF EXISTS { table } _float_uniform_1000" )
68+ self .env .cr .commit ()
5369
70+ def delete_indexes_many2one (self ):
5471 self .env .cr .execute ("DROP INDEX IF EXISTS perf_line_uniform_container_id" )
72+ self .env .cr .execute ("DROP INDEX IF EXISTS perf_line_parent_id" )
5573 self .env .cr .commit ()
5674
57- def create_indexes_uniform (self ):
58- for table in ['perf_line' , 'perf_container' , 'perf_tag' ]:
59- self .env .cr .execute (f"CREATE INDEX IF NOT EXISTS { table } _float_uniform_1000 ON { table } (float_uniform_1000)" )
60- self .env .cr .commit ()
75+ def delete_all_indexes (self ):
76+ self .delete_indexes_uniform ()
77+ self .delete_indexes_many2one ()
6178
6279 def test_create_indexes_many2one (self ):
6380 self .create_indexes_many2one ()
@@ -68,19 +85,23 @@ def test_create_indexes_uniform(self):
6885 def test_delete_all_indexes (self ):
6986 self .delete_all_indexes ()
7087
71- def test_one_level_condition (self ):
72- self .populate ()
88+ def test_truncate_everything (self ):
89+ print ('Truncate everything' )
90+ for table in ['perf_line' , 'perf_container' , 'perf_tag' ]:
91+ self .env .cr .execute (f'TRUNCATE "{ table } " RESTART IDENTITY CASCADE' )
7392 self .env .cr .commit ()
7493
75- ratios_possible = [
76- 500 , # 50 %
77- 50 , # 5 %
78- 5 , # 0.5 %
79- 0.5 , # 0.05 %
80- 0.01 , # 0.001 %
81- ]
94+ def test_one_sub_level_1_and (self ):
95+ self .test_truncate_everything ()
96+ self .populate ()
97+
98+ def domain_like (args ):
99+ return [
100+ '&' ,
101+ ('float_uniform_1000' , '<' , args [0 ]),
102+ ('uniform_container_id.float_uniform_1000' , '<' , args [1 ]),
103+ ]
82104
83- # For [<condition_1> line AND <condition_2> line.parent_id] on line
84105 queries = {
85106 'join' : """
86107SELECT "perf_line"."id"
@@ -91,6 +112,7 @@ def test_one_level_condition(self):
91112 "perf_container"."float_uniform_1000" < %s
92113ORDER BY "perf_line"."id"
93114 """ ,
115+
94116 'subselect' : """
95117SELECT "perf_line"."id"
96118FROM "perf_line"
@@ -99,6 +121,7 @@ def test_one_level_condition(self):
99121 "perf_line"."uniform_container_id" IN (SELECT "perf_container"."id" FROM "perf_container" WHERE "perf_container"."float_uniform_1000" < %s)
100122ORDER BY "perf_line"."id"
101123 """ ,
124+
102125 'exists' : """
103126SELECT "perf_line"."id"
104127FROM "perf_line"
@@ -109,31 +132,24 @@ def test_one_level_condition(self):
109132 """ ,
110133 }
111134
112- for line_ratio , container_ratio in combinations_with_replacement (ratios_possible , 2 ):
113- arguments = [line_ratio , container_ratio ]
114- res = None
115- for name , query in queries .items ():
116- self .env .cr .execute (query , arguments )
117- res_new = [id_ for id_ , in self .env .cr .fetchall ()]
118- if res and res != res_new :
119- raise Exception (f'{ name } not the same result than the previous one' )
135+ self .launch_queries (
136+ 'Level 1 - AND' ,
137+ queries ,
138+ combinations_with_replacement (ratios_possible , 2 ),
139+ domain_like ,
140+ )
120141
121- explain_dict = {}
122- for name , query in queries .items ():
123- self .env .cr .execute ("EXPLAIN " + query , arguments )
124- explain_dict [name ] = "\n " .join (s for s , in self .env .cr .fetchall ())
142+ def test_one_sub_level_1_or (self ):
143+ self .test_truncate_everything ()
144+ self .populate ()
125145
126- if len (set (explain_dict .values ())) == 1 :
127- print ("AND - For arguments - All the same" , arguments )
128- continue
129-
130- print ()
131- print ('AND - For arguments' , arguments )
132- for name , explain in explain_dict .items ():
133- print (f'=> { name } : ' )
134- print (explain )
146+ def domain_like (args ):
147+ return [
148+ '|' ,
149+ ('float_uniform_1000' , '<' , args [0 ]),
150+ ('uniform_container_id.float_uniform_1000' , '<' , args [1 ]),
151+ ]
135152
136- # For [<condition_1> line OR <condition_2> line.parent_id] on line
137153 queries = {
138154 'join' : """
139155SELECT "perf_line"."id"
@@ -144,6 +160,7 @@ def test_one_level_condition(self):
144160 "perf_container"."float_uniform_1000" < %s
145161ORDER BY "perf_line"."id"
146162 """ ,
163+
147164 'subselect' : """
148165SELECT "perf_line"."id"
149166FROM "perf_line"
@@ -152,6 +169,7 @@ def test_one_level_condition(self):
152169 "perf_line"."uniform_container_id" IN (SELECT "perf_container"."id" FROM "perf_container" WHERE "perf_container"."float_uniform_1000" < %s)
153170ORDER BY "perf_line"."id"
154171 """ ,
172+
155173 'exists' : """
156174SELECT "perf_line"."id"
157175FROM "perf_line"
@@ -162,25 +180,90 @@ def test_one_level_condition(self):
162180 """ ,
163181 }
164182
165- for line_ratio , container_ratio in combinations_with_replacement (ratios_possible , 2 ):
166- arguments = [line_ratio , container_ratio ]
183+ self .launch_queries (
184+ 'Level 1 - OR' ,
185+ queries ,
186+ combinations_with_replacement (ratios_possible , 2 ),
187+ domain_like ,
188+ )
189+
190+ def test_two_level_condition (self ):
191+ self .test_truncate_everything ()
192+ self .populate ()
193+
194+ def domain_like (args ):
195+ return [
196+ ('parent_id.uniform_container_id.float_uniform_1000' , '<' , args [0 ])
197+ ]
198+
199+ queries = {
200+ 'join' : """
201+ SELECT "perf_line"."id"
202+ FROM "perf_line"
203+ LEFT JOIN "perf_line" AS "perf_line__parent_id" ON "perf_line__parent_id"."id" = "perf_line"."parent_id"
204+ LEFT JOIN "perf_container" ON "perf_line__parent_id"."uniform_container_id" = "perf_container"."id"
205+ WHERE
206+ "perf_container"."float_uniform_1000" < %s
207+ ORDER BY "perf_line"."id"
208+ """ ,
209+
210+ 'subselect' : """
211+ SELECT "perf_line"."id"
212+ FROM "perf_line"
213+ WHERE
214+ "perf_line"."parent_id" IN (
215+ SELECT "perf_line"."id" FROM "perf_line"
216+ WHERE "perf_line"."uniform_container_id" IN (
217+ SELECT "perf_container"."id" FROM "perf_container"
218+ WHERE "perf_container"."float_uniform_1000" < %s
219+ )
220+ )
221+ ORDER BY "perf_line"."id"
222+ """ ,
223+
224+ 'exists' : """
225+ SELECT "perf_line"."id"
226+ FROM "perf_line"
227+ WHERE
228+ EXISTS (
229+ SELECT FROM "perf_line" AS "perf_line__parent_id"
230+ WHERE EXISTS (
231+ SELECT FROM "perf_container"
232+ WHERE "perf_container"."float_uniform_1000" < %s AND "perf_line__parent_id"."uniform_container_id" = "perf_container"."id"
233+ ) AND "perf_line"."parent_id" = "perf_line__parent_id"."id"
234+ )
235+ ORDER BY "perf_line"."id"
236+ """ ,
237+ }
238+
239+ self .launch_queries (
240+ 'Level 2 - For arguments' ,
241+ queries ,
242+ combinations_with_replacement (ratios_possible , 1 ),
243+ domain_like ,
244+ )
245+
246+ def launch_queries (self , name_test , queries , args_combinaison , domain_like : callable ):
247+ for arguments in args_combinaison :
167248 res = None
168249 for name , query in queries .items ():
169250 self .env .cr .execute (query , arguments )
170251 res_new = [id_ for id_ , in self .env .cr .fetchall ()]
171252 if res and res != res_new :
172- raise Exception (f'{ name } not the same result than the previous one' )
253+ raise Exception (f'{ name } not the same result than the previous one: { name_test } ' )
254+ res = res_new
173255
174256 explain_dict = {}
175257 for name , query in queries .items ():
176258 self .env .cr .execute ("EXPLAIN " + query , arguments )
177259 explain_dict [name ] = "\n " .join (s for s , in self .env .cr .fetchall ())
178260
179- if set (explain_dict .values ()) == 1 :
261+ if len (set (explain_dict .values ())) == 1 :
262+ print (f"{ name_test } : { domain_like (arguments )} : ALL the same" )
180263 continue
181264
182265 print ()
183- print ('OR - For arguments' , arguments )
266+ print (f" { name_test } : { domain_like ( arguments ) } " )
184267 for name , explain in explain_dict .items ():
185268 print (f'=> { name } : ' )
186269 print (explain )
0 commit comments