import hashlib from .utils import ( detect_fts, detect_primary_keys, detect_spatialite, escape_sqlite, get_all_foreign_keys, sqlite3, table_columns, ) HASH_BLOCK_SIZE = 1024 * 1024 def inspect_hash(path): """Calculate the hash of a database, efficiently.""" m = hashlib.sha256() with path.open("rb") as fp: while True: data = fp.read(HASH_BLOCK_SIZE) if not data: break m.update(data) return m.hexdigest() def inspect_views(conn): """List views in a database.""" return [ v[0] for v in conn.execute('select name from sqlite_master where type = "view"') ] def inspect_tables(conn, database_metadata): """List tables and their row counts, excluding uninteresting tables.""" tables = {} table_names = [ r["name"] for r in conn.execute('select * from sqlite_master where type="table"') ] for table in table_names: table_metadata = database_metadata.get("tables", {}).get(table, {}) try: count = conn.execute( f"select count(*) from {escape_sqlite(table)}" ).fetchone()[0] except sqlite3.OperationalError: # This can happen when running against a FTS virtual table # e.g. "select count(*) from some_fts;" count = 0 column_names = table_columns(conn, table) tables[table] = { "name": table, "columns": column_names, "primary_keys": detect_primary_keys(conn, table), "count": count, "hidden": table_metadata.get("hidden") or False, "fts_table": detect_fts(conn, table), } foreign_keys = get_all_foreign_keys(conn) for table, info in foreign_keys.items(): tables[table]["foreign_keys"] = info # Mark tables 'hidden' if they relate to FTS virtual tables hidden_tables = [r["name"] for r in conn.execute(""" select name from sqlite_master where rootpage = 0 and sql like '%VIRTUAL TABLE%USING FTS%' """)] if detect_spatialite(conn): # Also hide Spatialite internal tables hidden_tables += [ "ElementaryGeometries", "SpatialIndex", "geometry_columns", "spatial_ref_sys", "spatialite_history", "sql_statements_log", "sqlite_sequence", "views_geometry_columns", "virts_geometry_columns", ] + [ r["name"] for r in conn.execute(""" select name from sqlite_master where name like "idx_%" and type = "table" """) ] for t, table_info in tables.items(): for hidden_table in hidden_tables: if t == hidden_table or t.startswith(hidden_table): table_info["hidden"] = True continue return tables