From 8b7a39416739e827aad01677becb26f15a6cb184 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aur=C3=A9lien=20=28avd=29?= <avd@odoo.com>
Date: Wed, 24 Aug 2022 13:33:38 +0000
Subject: [PATCH] [FIX] res_currency: add order by to _select_companies_rates
 query

PG12 introduced an optimization for CTEs that automatically inlines
CTEs if they are only refered once in the parent query. Prior to that
CTEs were always materialzed, meaning that PG created a sort of temp
table on the fly to store the result of the CTE's evaluation.

Whereas this leads to performance improvements in general, in the
particular case of _select_companies_rates this inlining becomes a
performance bottleneck. This is because while the currency_rate CTE
is only refered once in both purchase_report and product_margin,
the join condition (cr.date_end is null or cr.date_end > ...)
requires evaluating the CTE's date_end subquery twice. This, combined
with the fact that in PG12 the planner goes for a Nested Loop JOIN instead
of a HASH Join in PG10 makes the performances of the whole query
much worse in PG12 than in PG10.

Adding an ORDER BY (or an OFFSET 0, the resulting plan is the same)
creates a kind of optimization fence that forces PG to evaluate the
subquery first using its own plan. This removes the need to rescan the
subquery each time the Merge JOIN filter has to be applied, which
is a good strategy in this specific situation.

The same result could be achieved by adding the keyword "MATERIALIZED"
in the CTE definition. The issue is that this keyword did not exist
in PG 10 so using it would require to check the PG version at runtime
from python.

Examples of query timings change before and after PR:

Number of POs | Before PR | After PR
      2000    |     7s    |    345ms
      7000    |     23s   |    1.1s

opw-2930578

closes odoo/odoo#98844

Signed-off-by: Raphael Collet <rco@odoo.com>
---
 odoo/addons/base/models/res_currency.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/odoo/addons/base/models/res_currency.py b/odoo/addons/base/models/res_currency.py
index 87ef31dd8311..fdb9f704f696 100644
--- a/odoo/addons/base/models/res_currency.py
+++ b/odoo/addons/base/models/res_currency.py
@@ -234,6 +234,7 @@ class Currency(models.Model):
                  LIMIT 1) AS date_end
             FROM res_currency_rate r
             JOIN res_company c ON (r.company_id is null or r.company_id = c.id)
+            ORDER BY date_end
         """
 
 
-- 
GitLab