From c8e1b3296496d33e109519d20cfd4f638fff1108 Mon Sep 17 00:00:00 2001 From: Eric Antones Date: Tue, 7 Jul 2026 12:41:27 +0200 Subject: [PATCH 1/2] [OU-FIX] base: make the legacy module-state snapshot resume-friendly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 18.0.1.3 pre-migration CREATEs the legacy ir_module_module snapshot table unguarded. When a run crashes after this script's transaction was committed but before base's version bump (e.g. an external-dependency UserError at button_upgrade), the standard restart-and-resume re-runs the script and dies on DuplicateTable. Use CREATE TABLE IF NOT EXISTS so the resume keeps the first capture — which is the correct pre-upgrade snapshot: recapturing at that point would record module states with the renames/merges below already applied. The rest of the script is already re-run tolerant (update_module_names no-ops on absent old names). --- openupgrade_scripts/scripts/base/18.0.1.3/pre-migration.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openupgrade_scripts/scripts/base/18.0.1.3/pre-migration.py b/openupgrade_scripts/scripts/base/18.0.1.3/pre-migration.py index c9dc9c8a4624..08dfac5a04c8 100644 --- a/openupgrade_scripts/scripts/base/18.0.1.3/pre-migration.py +++ b/openupgrade_scripts/scripts/base/18.0.1.3/pre-migration.py @@ -64,10 +64,15 @@ def _fix_company_layout_background(cr): @openupgrade.migrate(use_env=False) def migrate(cr, version): + # IF NOT EXISTS: on a resumed run (crash after this script's work was + # committed but before base's version bump) the table already holds the + # correct pre-upgrade snapshot — recreating it here would either crash + # (DuplicateTable) or, worse, recapture states with the renames/merges + # below already applied. Keep the first capture. openupgrade.logged_query( cr, f""" - CREATE TABLE {openupgrade.get_legacy_name("ir_module_module") + CREATE TABLE IF NOT EXISTS {openupgrade.get_legacy_name("ir_module_module") } AS (SELECT name, state FROM ir_module_module); """, ) From cac9c00115f6060e2981f807022ed250a25c762d Mon Sep 17 00:00:00 2001 From: Eric Antones Date: Tue, 7 Jul 2026 12:44:20 +0200 Subject: [PATCH 2/2] [OU-FIX] base: guard the view_mode legacy column copy on resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second statement of the same resume scenario: copy_columns ADDs the legacy ir_act_window_view.view_mode column unguarded, so a resumed run that already committed it dies on DuplicateColumn. Skip the copy when the legacy column already exists — its content from the first attempt is the correct pre-upgrade capture. The remaining statements of this script are already re-run tolerant (WHERE-guarded UPDATEs and no-op renames). --- .../scripts/base/18.0.1.3/pre-migration.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/openupgrade_scripts/scripts/base/18.0.1.3/pre-migration.py b/openupgrade_scripts/scripts/base/18.0.1.3/pre-migration.py index 08dfac5a04c8..6bffb97ae840 100644 --- a/openupgrade_scripts/scripts/base/18.0.1.3/pre-migration.py +++ b/openupgrade_scripts/scripts/base/18.0.1.3/pre-migration.py @@ -80,10 +80,15 @@ def migrate(cr, version): openupgrade.update_module_names(cr, merged_modules.items(), merge_modules=True) openupgrade.clean_transient_models(cr) openupgrade.rename_xmlids(cr, _renamed_xmlids) - openupgrade.copy_columns( - cr, - {"ir_act_window_view": [("view_mode", None, None)]}, - ) + # Same resume story as the snapshot table above: the legacy column may + # already exist (and be filled) from a previously committed attempt. + if not openupgrade.column_exists( + cr, "ir_act_window_view", openupgrade.get_legacy_name("view_mode") + ): + openupgrade.copy_columns( + cr, + {"ir_act_window_view": [("view_mode", None, None)]}, + ) old_column = openupgrade.get_legacy_name("view_mode") openupgrade.map_values( cr,