gen_sqlite_testdata.sh (2962B)
1 #!/bin/sh 2 # This file is part of libextractor. 3 # Copyright (C) 2026 Vidyut Samanta and Christian Grothoff 4 # 5 # Regenerate src/plugins/testdata/sqlite_test.db. 6 # 7 # Requires: sqlite3 (Debian package `sqlite3'), python3. 8 # 9 # The result is byte-for-byte reproducible: SQLite does not put a 10 # timestamp in the file, and the two header words that would otherwise 11 # vary (the SQLite library version at offset 96 and the version-valid-for 12 # counter at 92) are overwritten afterwards so that the test can assert 13 # on an exact version string regardless of the sqlite3 that ran here. 14 # 15 # The database is deliberately tiny: 1 KiB pages, a handful of rows, and 16 # a DELETE that is *not* followed by VACUUM so that freelist pages -- the 17 # place deleted records survive -- are present in the header. 18 set -e 19 20 srcdir=$(dirname "$0")/.. 21 out="$srcdir/src/plugins/testdata/sqlite_test.db" 22 23 rm -f "$out" "$out-journal" "$out-wal" "$out-shm" 24 25 sqlite3 "$out" <<'EOF' 26 PRAGMA page_size = 1024; 27 PRAGMA encoding = 'UTF-8'; 28 PRAGMA auto_vacuum = 2; -- incremental: keeps the freelist around 29 CREATE TABLE messages (id INTEGER PRIMARY KEY, handle TEXT, body TEXT); 30 CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER, path TEXT); 31 CREATE INDEX messages_handle ON messages (handle); 32 INSERT INTO messages (id, handle, body) VALUES 33 (1, 'alice@example.org', 'the first message'), 34 (2, 'bob@example.org', 'the second message'), 35 (3, 'carol@example.org', 'the third message'), 36 (4, 'dave@example.org', 'the fourth message'), 37 (5, 'erin@example.org', 'the fifth message'); 38 INSERT INTO attachments (id, message_id, path) VALUES 39 (1, 1, '/var/mobile/Media/DCIM/100APPLE/IMG_0001.JPG'), 40 (2, 3, '/var/mobile/Media/DCIM/100APPLE/IMG_0002.JPG'); 41 DELETE FROM messages WHERE id IN (2, 4); 42 DROP INDEX messages_handle; 43 PRAGMA user_version = 4242; 44 PRAGMA application_id = 1279613012; 45 PRAGMA journal_mode = WAL; 46 EOF 47 48 # 1279613012 == 0x4c455854 == "LEXT"; an arbitrary but printable tag, so 49 # that the plugin's four-character rendering of the application id is 50 # exercised as well as the numeric one. WAL mode is set last and leaves 51 # the write/read format versions at 2 in the closed database, which is 52 # what the plugin reports as the journal mode. 53 54 python3 - "$out" <<'EOF' 55 import struct 56 import sys 57 58 path = sys.argv[1] 59 with open(path, "r+b") as f: 60 data = bytearray(f.read()) 61 if len(data) < 100 or bytes(data[0:16]) != b"SQLite format 3\x00": 62 raise SystemExit("not a SQLite database: %s" % path) 63 # Pin the writer version to 3.45.1 (3 * 1000000 + 45 * 1000 + 1) and 64 # make version-valid-for agree with the change counter, so the file 65 # does not depend on the sqlite3 that generated it. 66 struct.pack_into(">I", data, 96, 3045001) 67 struct.pack_into(">I", data, 92, struct.unpack_from(">I", data, 24)[0]) 68 f.seek(0) 69 f.write(data) 70 f.truncate() 71 print("wrote %s (%d bytes)" % (path, len(data))) 72 EOF 73 74 rm -f "$out-journal" "$out-wal" "$out-shm"