1"""Reading a notebook cache (schema 15) from outside: its section images, stored as 16 KiB
2chunk rows, and the state of its write-ahead log."""
3import struct
4
5
6def image(connection, table='base'):
7 """The `base` image the queued edits apply to, or the observed `remote` image (None
8 unless a conflict or uncertain attempt holds one)."""
9 chunks = connection.execute(f'SELECT chunk, bytes FROM {table} ORDER BY chunk').fetchall()
10 assert [chunk for chunk, _ in chunks] == list(range(len(chunks))), 'Damaged cached image'
11 return b''.join(bytes(data) for _, data in chunks) or None
12
13
14def wal_commits(path):
15 """Commits in `<cache>-wal` since its last reset: frames whose header records the
16 database size after them, among the frames carrying the log's current salts. Frames
17 after the last commit belong to a transaction that has not committed."""
18 try:
19 data = path.with_name(path.name + '-wal').read_bytes()
20 except FileNotFoundError:
21 return 0, 0
22 if len(data) < 32:
23 return 0, 0
24 magic, _, page_size, _, salt1, salt2 = struct.unpack_from('>IIIIII', data)
25 assert magic in (0x377f0682, 0x377f0683), 'Not a SQLite write-ahead log'
26 commits = pending = 0
27 at = 32
28 while at + 24 + page_size <= len(data):
29 _, committed, frame1, frame2 = struct.unpack_from('>IIII', data, at)
30 if (frame1, frame2) != (salt1, salt2):
31 break
32 if committed:
33 commits, pending = commits + 1, 0
34 else:
35 pending += 1
36 at += 24 + page_size
37 return commits, pending