use binary stdin on py3k
[~helmut/debian-dedup.git] / update_sharing.py
1 #!/usr/bin/python
2
3 import optparse
4 import sqlite3
5
6 from dedup.utils import fetchiter
7
8 def add_values(cursor, insert_key, files, size):
9     cursor.execute("UPDATE sharing SET files = files + ?, size = size + ? WHERE pid1 = ? AND pid2 = ? AND fid1 = ? AND fid2 = ?;",
10                    (files, size) + insert_key)
11     if cursor.rowcount > 0:
12         return
13     cursor.execute("INSERT INTO sharing (pid1, pid2, fid1, fid2, files, size) VALUES (?, ?, ?, ?, ?, ?);",
14                    insert_key + (files, size))
15
16 def compute_pkgdict(rows):
17     pkgdict = dict()
18     for pid, _, filename, size, fid in rows:
19         funcdict = pkgdict.setdefault(pid, {})
20         funcdict.setdefault(fid, []).append((size, filename))
21     return pkgdict
22
23 def process_pkgdict(cursor, pkgdict):
24     for pid1, funcdict1 in pkgdict.items():
25         for fid1, files in funcdict1.items():
26             numfiles = len(files)
27             size = sum(entry[0] for entry in files)
28             for pid2, funcdict2 in pkgdict.items():
29                 if pid1 == pid2:
30                     pkgnumfiles = numfiles - 1
31                     pkgsize = size - min(entry[0] for entry in files)
32                     if pkgnumfiles == 0:
33                         continue
34                 else:
35                     pkgnumfiles = numfiles
36                     pkgsize = size
37                 for fid2 in funcdict2.keys():
38                     insert_key = (pid1, pid2, fid1, fid2)
39                     add_values(cursor, insert_key, pkgnumfiles, pkgsize)
40
41 def main(db):
42     cur = db.cursor()
43     cur.execute("PRAGMA foreign_keys = ON;")
44     cur.execute("DELETE FROM sharing;")
45     cur.execute("DELETE FROM duplicate;")
46     cur.execute("DELETE FROM issue;")
47     readcur = db.cursor()
48     readcur.execute("SELECT hash FROM hash GROUP BY hash HAVING count(*) > 1;")
49     for hashvalue, in fetchiter(readcur):
50         cur.execute("SELECT function.eqclass, content.pid, content.id, content.filename, content.size, hash.fid FROM hash JOIN content ON hash.cid = content.id JOIN function ON hash.fid = function.id AND function.eqclass IS NOT NULL WHERE hash = ?;",
51                     (hashvalue,))
52         rowdict = dict()
53         for row in cur.fetchall():
54             rowdict.setdefault(row[0], []).append(row[1:])
55         for eqclass, rows in rowdict.items():
56             if len(rows) < 2:
57                 print("skipping hash %s class %d with too few entries" % (hashvalue, eqclass))
58                 continue
59             print("processing hash %s class %d with %d entries" % (hashvalue, eqclass, len(rows)))
60             pkgdict = compute_pkgdict(rows)
61             cur.executemany("INSERT OR IGNORE INTO duplicate (cid) VALUES (?);",
62                             [(row[1],) for row in rows])
63             process_pkgdict(cur, pkgdict)
64     cur.execute("INSERT INTO issue (cid, issue) SELECT content.id, 'file named something.gz is not a valid gzip file' FROM content WHERE content.filename LIKE '%.gz' AND NOT EXISTS (SELECT 1 FROM hash JOIN function ON hash.fid = function.id WHERE hash.cid = content.id AND function.name = 'gzip_sha512');")
65     cur.execute("INSERT INTO issue (cid, issue) SELECT content.id, 'png image not named something.png' FROM content JOIN hash ON content.id = hash.cid JOIN function ON hash.fid = function.id WHERE function.name = 'png_sha512' AND lower(filename) NOT LIKE '%.png';")
66     cur.execute("INSERT INTO issue (cid, issue) SELECT content.id, 'gif image not named something.gif' FROM content JOIN hash ON content.id = hash.cid JOIN function ON hash.fid = function.id WHERE function.name = 'gif_sha512' AND lower(filename) NOT LIKE '%.gif';")
67     db.commit()
68
69 if __name__ == "__main__":
70     parser = optparse.OptionParser()
71     parser.add_option("-d", "--database", action="store",
72                       default="test.sqlite3",
73                       help="path to the sqlite3 database file")
74     options, args = parser.parse_args()
75     main(sqlite3.connect(options.database))