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()