Files
WPGSKD/scripts/MergeKeyStores.py
2026-08-10 22:56:26 +08:00

155 lines
4.8 KiB
Python

#!/usr/bin/env python3
import argparse
import sqlite3
import os
import sys
# Add path to import AtomicSQL
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from wpgskd.core.atomic_sql import AtomicSQL
"""
Merge multiple Key Store DBs into one.
Correctly handles multi-table structure (one table per service).
Dynamically handles missing 'title' columns in input DB.
"""
parser = argparse.ArgumentParser(
"Key Store DB merger",
description="Script to merge one key store db into another"
)
parser.add_argument(
"-i", "--input",
help="key store db that will send keys (Source)",
required=True)
parser.add_argument(
"-o", "--output",
help="key store db that will receive keys (Target)",
required=True)
args = parser.parse_args()
if not os.path.exists(args.input):
print(f"Input file not found: {args.input}")
sys.exit(1)
# Ensure output dir exists
os.makedirs(os.path.dirname(os.path.abspath(args.output)), exist_ok=True)
input_db = AtomicSQL()
input_id = input_db.load(sqlite3.connect(args.input))
output_db = AtomicSQL()
output_id = output_db.load(sqlite3.connect(args.output))
# 1. Get all table names from input DB
tables = input_db.safe_execute(
input_id,
lambda db, cursor: cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
).fetchall()
tables = [t[0] for t in tables]
print(f"Found tables in input DB: {tables}")
total_added = 0
total_updated = 0
total_skipped = 0
for table in tables:
print(f"\nProcessing table: {table}...")
# 2. Ensure table exists in output DB
# Get columns from input table
input_cols_info = input_db.safe_execute(
input_id,
lambda db, cursor: cursor.execute(f"PRAGMA table_info(`{table}`)")
).fetchall()
input_cols = [col[1] for col in input_cols_info]
# Check if table exists in output
out_table_exists = output_db.safe_execute(
output_id,
lambda db, cursor: cursor.execute("SELECT count(name) FROM sqlite_master WHERE type='table' AND name=?", [table])
).fetchone()[0] == 1
if not out_table_exists:
print(f" - Creating table {table} in output DB...")
output_db.safe_execute(
output_id,
lambda db, cursor: cursor.execute(
f"""
CREATE TABLE IF NOT EXISTS `{table}` (
"id" INTEGER NOT NULL UNIQUE,
"kid" TEXT NOT NULL COLLATE NOCASE,
"key_" TEXT NOT NULL COLLATE NOCASE,
"title" TEXT,
PRIMARY KEY("id" AUTOINCREMENT),
UNIQUE("kid", "key_")
);
"""
)
)
# 3. Fetch all rows from input table dynamically (handle missing title)
has_title = "title" in input_cols
if has_title:
select_query = f"SELECT kid, key_, title FROM `{table}`"
else:
select_query = f"SELECT kid, key_ FROM `{table}`"
print(f" - Notice: No 'title' column in source table '{table}', defaulting to None.")
rows = input_db.safe_execute(
input_id,
lambda db, cursor: cursor.execute(select_query)
).fetchall()
for row in rows:
kid = row[0]
key = row[1]
title = row[2] if has_title else None
# Check existence in output
exists = output_db.safe_execute(
output_id,
lambda db, cursor: cursor.execute(
f"SELECT title FROM `{table}` WHERE kid=? AND key_=?",
[kid, key]
)
).fetchone()
if exists:
# Update title if missing
current_title = exists[0]
if title and not current_title:
output_db.safe_execute(
output_id,
lambda db, cursor: cursor.execute(
f"UPDATE `{table}` SET title=? WHERE kid=? AND key_=?",
(title, kid, key)
)
)
total_updated += 1
else:
total_skipped += 1
else:
# Insert
output_db.safe_execute(
output_id,
lambda db, cursor: cursor.execute(
f"INSERT INTO `{table}` (kid, key_, title) VALUES (?, ?, ?)",
(kid, key, title)
)
)
total_added += 1
# print(f" Added {kid}")
output_db.commit(output_id)
print("\n" + "="*30)
print(f"Merge Complete!")
print(f"Added: {total_added}")
print(f"Updated: {total_updated}")
print(f"Skipped: {total_skipped}")
print("="*30)