93 lines
3.0 KiB
Python
93 lines
3.0 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import sqlite3
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
def digest(value: Any) -> str:
|
|
content = json.dumps(
|
|
value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), default=str
|
|
).encode("utf-8")
|
|
return hashlib.sha256(content).hexdigest()
|
|
|
|
|
|
def schema(connection: sqlite3.Connection) -> list[dict[str, Any]]:
|
|
rows = connection.execute(
|
|
"""
|
|
SELECT type, name, tbl_name, sql
|
|
FROM sqlite_master
|
|
WHERE name NOT LIKE 'sqlite_%'
|
|
ORDER BY type, name
|
|
"""
|
|
).fetchall()
|
|
return [dict(row) for row in rows]
|
|
|
|
|
|
def table_rows(connection: sqlite3.Connection, table: str) -> list[dict[str, Any]]:
|
|
quoted = '"' + table.replace('"', '""') + '"'
|
|
rows = [dict(row) for row in connection.execute(f"SELECT * FROM {quoted}").fetchall()]
|
|
return sorted(rows, key=lambda row: json.dumps(row, ensure_ascii=False, sort_keys=True, default=str))
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description="Compare preservation SQLite databases")
|
|
parser.add_argument("--original", type=Path, required=True)
|
|
parser.add_argument("--migrated", type=Path, required=True)
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
parser.add_argument("tables", nargs="+")
|
|
args = parser.parse_args()
|
|
|
|
original = sqlite3.connect(args.original)
|
|
migrated = sqlite3.connect(args.migrated)
|
|
original.row_factory = sqlite3.Row
|
|
migrated.row_factory = sqlite3.Row
|
|
try:
|
|
original_schema = schema(original)
|
|
migrated_schema = schema(migrated)
|
|
tables = []
|
|
all_equal = original_schema == migrated_schema
|
|
for table in args.tables:
|
|
original_rows = table_rows(original, table)
|
|
migrated_rows = table_rows(migrated, table)
|
|
equal = original_rows == migrated_rows
|
|
all_equal = all_equal and equal
|
|
tables.append(
|
|
{
|
|
"table": table,
|
|
"original_count": len(original_rows),
|
|
"migrated_count": len(migrated_rows),
|
|
"original_sha256": digest(original_rows),
|
|
"migrated_sha256": digest(migrated_rows),
|
|
"equal": equal,
|
|
}
|
|
)
|
|
result = {
|
|
"all_equal": all_equal,
|
|
"schema": {
|
|
"object_count": len(original_schema),
|
|
"original_sha256": digest(original_schema),
|
|
"migrated_sha256": digest(migrated_schema),
|
|
"equal": original_schema == migrated_schema,
|
|
},
|
|
"tables": tables,
|
|
}
|
|
finally:
|
|
original.close()
|
|
migrated.close()
|
|
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
args.output.write_text(
|
|
json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
|
|
)
|
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
if not all_equal:
|
|
raise SystemExit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|