feat: Add two new scripts to remove the product_pricelist_res_config_settin_res_config_settings_id_fkey zombie constraint, one using Odoo's ORM and another using direct psycopg2 connection.
This commit is contained in:
parent
add87ea10d
commit
6fa8d55e7e
86
delete_zombie_constraint.py
Normal file
86
delete_zombie_constraint.py
Normal file
@ -0,0 +1,86 @@
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Drop Zombie Constraint 'product_pricelist_res_config_settin_res_config_settings_id_fkey'")
|
||||
parser.add_argument("odoo_bin_path", help="Path to odoo-bin executable (e.g. odoo/odoo-bin)")
|
||||
parser.add_argument("conf_path", help="Path to odoo.conf")
|
||||
parser.add_argument("db_name", help="Database name")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
odoo_bin_path = os.path.abspath(args.odoo_bin_path)
|
||||
conf_path = os.path.abspath(args.conf_path)
|
||||
db_name = args.db_name
|
||||
|
||||
# Add Odoo to sys.path
|
||||
odoo_root = os.path.dirname(odoo_bin_path)
|
||||
if odoo_root not in sys.path:
|
||||
sys.path.append(odoo_root)
|
||||
|
||||
# Change CWD to config directory to handle relative paths in config
|
||||
os.chdir(os.path.dirname(conf_path))
|
||||
|
||||
try:
|
||||
import odoo
|
||||
from odoo import api, SUPERUSER_ID
|
||||
except ImportError:
|
||||
print(f"Error: Could not import 'odoo' module from {odoo_root}. Make sure odoo-bin path is correct.")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Initializing Odoo Environment for database: {db_name}...")
|
||||
try:
|
||||
odoo.tools.config.parse_config(['-c', conf_path])
|
||||
registry = odoo.registry(db_name)
|
||||
except Exception as e:
|
||||
print(f"Error initializing Odoo: {e}")
|
||||
return
|
||||
|
||||
with registry.cursor() as cr:
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
print("Connected to Odoo.")
|
||||
|
||||
# Define the problematic constraint connection
|
||||
constraint_name = 'product_pricelist_res_config_settin_res_config_settings_id_fkey'
|
||||
|
||||
# Check if exists
|
||||
cr.execute("""
|
||||
SELECT conrelid::regclass::text
|
||||
FROM pg_constraint
|
||||
WHERE conname = %s
|
||||
""", (constraint_name,))
|
||||
result = cr.fetchone()
|
||||
|
||||
if result:
|
||||
table_name = result[0]
|
||||
print(f"Found constraint '{constraint_name}' on table '{table_name}'.")
|
||||
try:
|
||||
print(f"Dropping table '{table_name}'...")
|
||||
cr.execute(f"DROP TABLE IF EXISTS {table_name} CASCADE")
|
||||
print("Table dropped.")
|
||||
|
||||
# Double check constraint
|
||||
cr.execute("SELECT count(*) FROM pg_constraint WHERE conname = %s", (constraint_name,))
|
||||
if cr.fetchone()[0] == 0:
|
||||
print("Constraint successfully removed.")
|
||||
cr.commit()
|
||||
else:
|
||||
print("WARNING: Constraint still exists! Rolled back.")
|
||||
cr.rollback()
|
||||
except Exception as e:
|
||||
print(f"Error dropping table/constraint: {e}")
|
||||
cr.rollback()
|
||||
else:
|
||||
print(f"Constraint '{constraint_name}' not found. Database is likely clean.")
|
||||
# Also invoke the hook logic just in case the name varies?
|
||||
# The hook used 'DROP TABLE IF EXISTS product_pricelist_res_config_settings_rel CASCADE'
|
||||
# Let's try that too.
|
||||
print("Attempting to drop 'product_pricelist_res_config_settings_rel' blindly just in case...")
|
||||
cr.execute("DROP TABLE IF EXISTS product_pricelist_res_config_settings_rel CASCADE")
|
||||
print("Done.")
|
||||
cr.commit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
87
delete_zombie_constraint_direct.py
Normal file
87
delete_zombie_constraint_direct.py
Normal file
@ -0,0 +1,87 @@
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import configparser
|
||||
import psycopg2
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Drop Zombie Constraint directly via psycopg2")
|
||||
parser.add_argument("conf_path", help="Path to odoo.conf")
|
||||
parser.add_argument("db_name", help="Database name")
|
||||
|
||||
args = parser.parse_args()
|
||||
conf_path = os.path.abspath(args.conf_path)
|
||||
db_name = args.db_name
|
||||
|
||||
if not os.path.exists(conf_path):
|
||||
print(f"Error: Config file not found at {conf_path}")
|
||||
sys.exit(1)
|
||||
|
||||
# Parse config
|
||||
config = configparser.ConfigParser()
|
||||
config.read(conf_path)
|
||||
|
||||
if 'options' not in config:
|
||||
print("Error: 'options' section not found in config file.")
|
||||
sys.exit(1)
|
||||
|
||||
options = config['options']
|
||||
db_host = options.get('db_host', 'localhost')
|
||||
db_port = options.get('db_port', '5432')
|
||||
db_user = options.get('db_user', 'odoo')
|
||||
db_password = options.get('db_password', '')
|
||||
|
||||
print(f"Connecting to database '{db_name}' on {db_host}:{db_port} as user '{db_user}'...")
|
||||
|
||||
try:
|
||||
conn = psycopg2.connect(
|
||||
dbname=db_name,
|
||||
user=db_user,
|
||||
password=db_password,
|
||||
host=db_host,
|
||||
port=db_port
|
||||
)
|
||||
conn.autocommit = False # Use transaction
|
||||
cur = conn.cursor()
|
||||
print("Connected successfully.")
|
||||
|
||||
constraint_name = 'product_pricelist_res_config_settin_res_config_settings_id_fkey'
|
||||
|
||||
# Check for constraint existence
|
||||
cur.execute("SELECT conrelid::regclass::text FROM pg_constraint WHERE conname = %s", (constraint_name,))
|
||||
result = cur.fetchone()
|
||||
|
||||
if result:
|
||||
table_name = result[0]
|
||||
print(f"Found constraint '{constraint_name}' on table '{table_name}'.")
|
||||
|
||||
# Drop the table (cascade to remove constraint)
|
||||
print(f"Dropping table '{table_name}'...")
|
||||
cur.execute(f"DROP TABLE IF EXISTS {table_name} CASCADE")
|
||||
|
||||
# Verify removal
|
||||
cur.execute("SELECT count(*) FROM pg_constraint WHERE conname = %s", (constraint_name,))
|
||||
if cur.fetchone()[0] == 0:
|
||||
print("Table dropped and constraint removed.")
|
||||
conn.commit()
|
||||
print("Transaction committed.")
|
||||
else:
|
||||
print("ERROR: Constraint persists after drop! Rolling back.")
|
||||
conn.rollback()
|
||||
else:
|
||||
print(f"Constraint '{constraint_name}' not found.")
|
||||
# Optimization: Try dropping the likely table name anyway
|
||||
print("Attempting to drop 'product_pricelist_res_config_settings_rel' just in case...")
|
||||
cur.execute("DROP TABLE IF EXISTS product_pricelist_res_config_settings_rel CASCADE")
|
||||
conn.commit()
|
||||
print("Done.")
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Database error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user