libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

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"