37 lines
1.4 KiB
Python
37 lines
1.4 KiB
Python
"""Read-only structural fingerprints; no credentials or row contents in the report."""
|
|
import hashlib
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from sqlalchemy import create_engine, text
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
from app.core.config import get_settings
|
|
|
|
|
|
def fingerprint():
|
|
engine = create_engine(get_settings().mysql_dsn.replace("mysql+asyncmy", "mysql+pymysql"))
|
|
result = {}
|
|
with engine.connect() as connection:
|
|
names = connection.execute(text("""
|
|
SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE()
|
|
""")).scalars().all()
|
|
for name in sorted(names):
|
|
if name == "alembic_version":
|
|
continue
|
|
columns = connection.execute(text("""
|
|
SELECT COLUMN_NAME,COLUMN_TYPE,IS_NULLABLE,COLUMN_DEFAULT,EXTRA,
|
|
CHARACTER_SET_NAME,COLLATION_NAME,GENERATION_EXPRESSION
|
|
FROM information_schema.COLUMNS
|
|
WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=:name ORDER BY ORDINAL_POSITION
|
|
"""), {"name": name}).all()
|
|
canonical = json.dumps([list(row) for row in columns], ensure_ascii=False, default=str)
|
|
result[name] = hashlib.sha256(canonical.encode()).hexdigest()
|
|
engine.dispose()
|
|
return result
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print(json.dumps(fingerprint(), indent=2, ensure_ascii=False))
|