Skip to content

Commit d850f52

Browse files
committed
[FIX] pg: avoid dropping not null constraints in pg18
In PG18 not null constraint names follow the '{table}_{column}_not_null' pattern as opposed to the '[0-9_]+_not_null' one followed until pg17. Our utils must be adapted to cope with that, otherwise the util will attempt to drop not null constraints - failing against those of primary keys. The following traceback is achieved when trying to upgrade a new demo db in PG18 with `mrp` installed, from 17.0 to 18.0: ``` 2025-11-04 10:23:38,126 28873 ERROR test_mrp_17_18.0 odoo.sql_db: bad query: b'ALTER TABLE "mail_push" DROP CONSTRAINT IF EXISTS "mail_notification_web_push_id_not_null" ' ERROR: column "id" is in a primary key 2025-11-04 10:23:38,127 28873 WARNING test_mrp_17_18.0 odoo.modules.loading: Transient module states were reset 2025-11-04 10:23:38,129 28873 ERROR test_mrp_17_18.0 odoo.modules.registry: Failed to load registry 2025-11-04 10:23:38,129 28873 CRITICAL test_mrp_17_18.0 odoo.service.server: Failed to initialize database `test_mrp_17_18.0`. Traceback (most recent call last): File "/home/odoo/src/odoo/18.0/odoo/service/server.py", line 1366, in preload_registries registry = Registry.new(dbname, update_module=update_module) File "<decorator-gen-13>", line 2, in new File "/home/odoo/src/odoo/18.0/odoo/tools/func.py", line 97, in locked return func(inst, *args, **kwargs) File "/home/odoo/src/odoo/18.0/odoo/modules/registry.py", line 129, in new odoo.modules.load_modules(registry, force_demo, status, update_module) File "/home/odoo/src/odoo/18.0/odoo/modules/loading.py", line 485, in load_modules processed_modules += load_marked_modules(env, graph, File "/home/odoo/src/odoo/18.0/odoo/modules/loading.py", line 365, in load_marked_modules loaded, processed = load_module_graph( File "/home/odoo/src/odoo/18.0/odoo/modules/loading.py", line 182, in load_module_graph migrations.migrate_module(package, 'pre') File "/home/odoo/src/odoo/18.0/odoo/modules/migration.py", line 222, in migrate_module exec_script(self.cr, installed_version, pyfile, pkg.name, stage, stageformat[stage] % version) File "/home/odoo/src/odoo/18.0/odoo/modules/migration.py", line 259, in exec_script mod.migrate(cr, installed_version) File "/home/odoo/src/upgrade/migrations/mail/saas~17.1.1.16/pre-migrate.py", line 30, in migrate util.rename_model(cr, "mail.notification.web.push", "mail.push") File "/home/odoo/src/upgrade-util/src/util/models.py", line 308, in rename_model pg_rename_table(cr, old_table, new_table) File "/home/odoo/src/upgrade-util/src/util/pg.py", line 1354, in rename_table remove_constraint(cr, new_table, const, warn=False) File "/home/odoo/src/upgrade-util/src/util/pg.py", line 853, in remove_constraint cr.execute(format_query(cr, "ALTER TABLE {} DROP CONSTRAINT IF EXISTS {} {}", table, name, cascade)) File "/home/odoo/src/odoo/18.0/odoo/sql_db.py", line 357, in execute res = self._obj.execute(query, params) psycopg2.errors.InvalidTableDefinition: column "id" is in a primary key ```
1 parent 25cb373 commit d850f52

File tree

1 file changed

+17
-12
lines changed

1 file changed

+17
-12
lines changed

src/util/pg.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,14 +1333,14 @@ def rename_table(cr, old_table, new_table, remove_constraints=True):
13331333
)
13341334

13351335
if remove_constraints:
1336-
# DELETE all constraints, except Primary/Foreign keys, they will be re-created by the ORM
1337-
# NOTE: Custom constraints will instead be lost
1336+
# DELETE all constraints, except Primary/Foreign keys/not null checks, they will be re-created by the ORM
1337+
# NOTE: Custom constraints will instead be lost, except for not null ones
13381338
cr.execute(
1339-
"""
1339+
r"""
13401340
SELECT constraint_name
13411341
FROM information_schema.table_constraints
13421342
WHERE table_name = %s
1343-
AND constraint_name !~ '^[0-9_]+_not_null$'
1343+
AND constraint_name !~ '^\w+_not_null$'
13441344
AND ( constraint_type NOT IN ('PRIMARY KEY', 'FOREIGN KEY')
13451345
-- For long table names the constraint name is shortened by PG to fit 63 chars, in such cases
13461346
-- it's better to drop the constraint, even if it's a foreign key, and let the ORM re-create it.
@@ -1353,24 +1353,29 @@ def rename_table(cr, old_table, new_table, remove_constraints=True):
13531353
_logger.info("Dropping constraint %s on table %s", const, new_table)
13541354
remove_constraint(cr, new_table, const, warn=False)
13551355

1356-
# rename fkeys
1356+
# rename constraints
13571357
cr.execute(
13581358
"""
13591359
SELECT constraint_name
13601360
FROM information_schema.table_constraints
13611361
WHERE table_name = %s
1362-
AND constraint_type = 'FOREIGN KEY'
1363-
AND constraint_name LIKE %s
1362+
AND (
1363+
constraint_name ~ %s
1364+
OR (
1365+
constraint_type = 'FOREIGN KEY'
1366+
AND constraint_name LIKE %s
1367+
)
1368+
)
13641369
""",
1365-
[new_table, old_table.replace("_", r"\_") + r"\_%"],
1370+
[new_table, "^" + old_table + r"_\w+_not_null$", old_table.replace("_", r"\_") + r"\_%"],
13661371
)
13671372
old_table_length = len(old_table)
1368-
for (old_fkey,) in cr.fetchall():
1369-
new_fkey = new_table + old_fkey[old_table_length:]
1370-
_logger.info("Renaming FK %r to %r", old_fkey, new_fkey)
1373+
for (old_const,) in cr.fetchall():
1374+
new_const = new_table + old_const[old_table_length:]
1375+
_logger.info("Renaming constraint %r to %r", old_const, new_const)
13711376
cr.execute(
13721377
sql.SQL("ALTER TABLE {} RENAME CONSTRAINT {} TO {}").format(
1373-
sql.Identifier(new_table), sql.Identifier(old_fkey), sql.Identifier(new_fkey)
1378+
sql.Identifier(new_table), sql.Identifier(old_const), sql.Identifier(new_const)
13741379
)
13751380
)
13761381

0 commit comments

Comments
 (0)