feat(compact): Access database compact & repair API + CLI
- API: POST /api/compact (optional body {"files": [...]})
- CLI: python main.py compact [--db FILE]
- Local path: rename → CompactDatabase(bak→src) → delete bak (zero copy)
- UNC path: copy to local temp → compact → copy back (avoids DAO segfault)
- Uses DAO DBEngine.CompactDatabase via pywin32 COM
- new dep: pywin32>=306
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
15
main.py
15
main.py
@@ -5,6 +5,7 @@ Run from the project root (no ``-m`` needed)::
|
||||
python main.py fullsync [--db FILE] [--table NAME] [--clear-change-log]
|
||||
python main.py incremental [--loop] [--poll-interval N]
|
||||
python main.py compare [--granularity count|ids] [--db FILE] [--table NAME] [--report PATH]
|
||||
python main.py compact [--db FILE]
|
||||
|
||||
Configuration is hard-coded to ``config.yaml`` next to this script -- it is not
|
||||
a command-line argument, so all three blocks always use the same config (and
|
||||
@@ -29,6 +30,7 @@ from sync.logging_setup import setup_logging
|
||||
from sync.fullsync import full_sync
|
||||
from sync import service
|
||||
from sync.compare import compare, any_mismatch, format_report, write_report
|
||||
from sync.compact import compact_files, _human
|
||||
|
||||
CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.yaml")
|
||||
log = logging.getLogger("main")
|
||||
@@ -60,6 +62,9 @@ def _parse_args(argv):
|
||||
pc.add_argument("--table", help="limit to one table")
|
||||
pc.add_argument("--report", help="write the report to this file as well as stdout")
|
||||
|
||||
pcp = sub.add_parser("compact", help="compact & repair Access databases")
|
||||
pcp.add_argument("--db", help="limit to one Access file (by FileMapping.file)")
|
||||
|
||||
return p.parse_args(argv)
|
||||
|
||||
|
||||
@@ -98,6 +103,16 @@ def main(argv=None) -> int:
|
||||
service.cycle(cfg)
|
||||
return 0
|
||||
|
||||
if args.command == "compact":
|
||||
summary = compact_files(cfg, db_filter=args.db)
|
||||
for r in summary.results:
|
||||
if r.ok:
|
||||
print(f"[OK] {r.file} {_human(r.before_bytes)} -> {_human(r.after_bytes)} ({r.duration_s:.1f}s)")
|
||||
else:
|
||||
print(f"[FAIL] {r.file} {r.error}")
|
||||
print(f"--- {summary.ok} OK, {summary.failed} FAIL, saved {_human(summary.saved_bytes)} ---")
|
||||
return 0 if summary.all_ok else 1
|
||||
|
||||
if args.command == "compare":
|
||||
results = compare(cfg, granularity=args.granularity,
|
||||
db_filter=args.db, table_filter=args.table)
|
||||
|
||||
Reference in New Issue
Block a user