sqlite3_header/lib.rs
1// https://sqlite.org/fileformat2.html
2
3pub mod error;
4
5use std::convert::TryInto;
6
7/// The C string "SQLite format 3\000"
8const MAGIC_HEADER_BYTES: [u8; 16] = [
9 0x53, 0x51, 0x4c, 0x69,
10 0x74, 0x65, 0x20, 0x66,
11 0x6f, 0x72, 0x6d, 0x61,
12 0x74, 0x20, 0x33, 0x00,
13];
14
15fn two_byte_slice_to_u16(slice: &[u8]) -> u16 {
16 u16::from_be_bytes(slice.try_into().unwrap())
17}
18
19fn four_byte_slice_to_u32(slice: &[u8]) -> u32 {
20 u32::from_be_bytes(slice.try_into().unwrap())
21}
22
23/// The file format write version and file format read version at offsets 18 and 19
24/// are intended to allow for enhancements of the file format in future versions of
25/// SQLite. In current versions of SQLite, both of these values are 1 for rollback
26/// journalling modes and 2 for WAL journalling mode. If a version of SQLite coded
27/// to the current file format specification encounters a database file where the
28/// read version is 1 or 2 but the write version is greater than 2, then the
29/// database file must be treated as read-only. If a database file with a read
30/// version greater than 2 is encountered, then that database cannot be read or
31/// written.
32#[derive(Debug)]
33pub enum FileFormat {
34 Inaccessible,
35 Legacy,
36 WriteAheadLogging,
37}
38
39/// The maximum and minimum embedded payload fractions and the leaf payload
40/// fraction values must be 64, 32, and 32. These values were originally intended
41/// to be tunable parameters that could be used to modify the storage format of the
42/// b-tree algorithm. However, that functionality is not supported and there are no
43/// current plans to add support in the future. Hence, these three bytes are fixed
44/// at the values specified.
45#[derive(Debug)]
46pub struct Payload {
47 pub leaf_fraction: u8,
48 pub maximum_embedded_fraction: u8,
49 pub minimum_embedded_fraction: u8,
50}
51
52/// Unused pages in the database file are stored on a freelist. The 4-byte
53/// big-endian integer at offset 32 stores the page number of the first page of the
54/// freelist, or zero if the freelist is empty. The 4-byte big-endian integer at
55/// offset 36 stores stores the total number of pages on the freelist.
56#[derive(Debug)]
57pub struct Freelist {
58 pub page_index: u32,
59 pub count: u32,
60}
61
62/// The schema format number is a 4-byte big-endian integer at offset 44. The
63/// schema format number is similar to the file format read and write version
64/// numbers at offsets 18 and 19 except that the schema format number refers to the
65/// high-level SQL formatting rather than the low-level b-tree formatting. Four
66/// schema format numbers are currently defined:
67/// 1. Format 1 is understood by all versions of SQLite back to version 3.0.0 (2004-06-18).
68/// 2. Format 2 adds the ability of rows within the same table to have a varying number of columns, in order to support the ALTER TABLE ... ADD COLUMN functionality. Support for reading and writing format 2 was added in SQLite version 3.1.3 on 2005-02-20.
69/// 3. Format 3 adds the ability of extra columns added by ALTER TABLE ... ADD COLUMN to have non-NULL default values. This capability was added in SQLite version 3.1.4 on 2005-03-11.
70/// 4. Format 4 causes SQLite to respect the DESC keyword on index declarations. (The DESC keyword is ignored in indexes for formats 1, 2, and 3.) Format 4 also adds two new boolean record type values (serial types 8 and 9). Support for format 4 was added in SQLite 3.3.0 on 2006-01-10.
71/// New database files created by SQLite use format 4 by default. The
72/// legacy_file_format pragma can be used to cause SQLite to create new database
73/// files using format 1. The format version number can be made to default to 1
74/// instead of 4 by setting SQLITE_DEFAULT_FILE_FORMAT=1 at compile-time.
75#[derive(Debug)]
76pub enum SchemaFormat {
77 Format1,
78 Format2,
79 Format3,
80 Format4,
81}
82
83#[derive(Debug)]
84pub struct Schema {
85 /// The schema cookie is a 4-byte big-endian integer at offset 40 that is
86 /// incremented whenever the database schema changes. A prepared statement is
87 /// compiled against a specific version of the database schema. When the database
88 /// schema changes, the statement must be reprepared. When a prepared statement
89 /// runs, it first checks the schema cookie to ensure the value is the same as when
90 /// the statement was prepared and if the schema cookie has changed, the statement
91 /// either automatically reprepares and reruns or it aborts with an SQLITE_SCHEMA
92 /// error.
93 pub cookie: u32,
94 pub format: SchemaFormat,
95}
96
97/// The 4-byte big-endian integer at offset 56 determines the encoding used for all text strings
98/// stored in the database. A value of 1 means UTF-8. A value of 2 means UTF-16le. A value of 3 means
99/// UTF-16be. No other values are allowed. The sqlite3.h header file defines C-preprocessor macros
100/// SQLITE_UTF8 as 1, SQLITE_UTF16LE as 2, and SQLITE_UTF16BE as 3, to use in place of the numeric
101/// codes for the text encoding.
102#[derive(Debug)]
103pub enum DatabaseTextEncoding {
104 Utf8,
105 Utf16le,
106 Utf16be,
107}
108
109#[derive(Debug)]
110pub enum VacuumMode {
111 Auto,
112 Incremental,
113}
114
115/// The two 4-byte big-endian integers at offsets 52 and 64 are used to manage the
116/// auto_vacuum and incremental_vacuum modes. If the integer at offset 52 is zero
117/// then pointer-map (ptrmap) pages are omitted from the database file and neither
118/// auto_vacuum nor incremental_vacuum are supported. If the integer at offset 52 is
119/// non-zero then it is the page number of the largest root page in the database
120/// file, the database file will contain ptrmap pages, and the mode must be either
121/// auto_vacuum or incremental_vacuum. In this latter case, the integer at offset 64
122/// is true for incremental_vacuum and false for auto_vacuum. If the integer at
123/// offset 52 is zero then the integer at offset 64 must also be zero.
124#[derive(Debug)]
125pub struct Vacuum {
126 pub largest_root_btree_page: u32,
127 pub mode: VacuumMode,
128}
129
130/// The 4-byte big-endian integer at offset 96 stores the SQLITE_VERSION_NUMBER
131/// value for the SQLite library that most recently modified the database file. The
132/// 4-byte big-endian integer at offset 92 is the value of the change counter when
133/// the version number was stored. The integer at offset 92 indicates which
134/// transaction the version number is valid for and is sometimes called the
135/// "version-valid-for number".
136#[derive(Debug)]
137pub struct LastUpdate {
138 pub sqlite_version_number: u32,
139 pub version_valid_for: u32,
140}
141
142#[derive(Debug)]
143pub struct SQLite3Header {
144 page_size: u16,
145
146 file_format_write_version: FileFormat,
147 file_format_read_version: FileFormat,
148
149 reserved_bytes_per_page: u8,
150
151 payload_fraction: Payload,
152
153 file_change_counter: u32,
154
155 in_header_database_size: u32,
156
157 freelist: Freelist,
158
159 schema: Schema,
160
161 default_page_cache_size: u32,
162
163 database_text_encoding: DatabaseTextEncoding,
164
165 user_version: u32,
166
167 vacuum: Option<Vacuum>,
168
169 application_id: u32,
170
171 last_update: LastUpdate,
172}
173
174impl SQLite3Header {
175 /// All other bytes of the database file header are reserved for future expansion
176 /// and must be set to zero.
177 #[allow(non_upper_case_globals)]
178 pub const reserved: [u8; 20] = [0; 20];
179
180 /// Every valid SQLite database file begins with the following 16 bytes (in hex):
181 /// 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00. This byte sequence corresponds
182 /// to the UTF-8 string "SQLite format 3" including the nul terminator character at
183 /// the end.
184 pub fn magic_header_string(&self) -> &str {
185 std::str::from_utf8(&MAGIC_HEADER_BYTES).unwrap()
186 }
187
188 /// The two-byte value beginning at offset 16 determines the page size of the
189 /// database. For SQLite versions 3.7.0.1 (2010-08-04) and earlier, this value is
190 /// interpreted as a big-endian integer and must be a power of two between 512 and
191 /// 32768, inclusive. Beginning with SQLite version 3.7.1 (2010-08-23), a page size
192 /// of 65536 bytes is supported. The value 65536 will not fit in a two-byte
193 /// integer, so to specify a 65536-byte page size, the value at offset 16 is 0x00
194 /// 0x01. This value can be interpreted as a big-endian 1 and thought of as a magic
195 /// number to represent the 65536 page size. Or one can view the two-byte field as
196 /// a little endian number and say that it represents the page size divided by 256.
197 /// These two interpretations of the page-size field are equivalent.
198 pub fn page_size(&self) -> u16 {
199 self.page_size
200 }
201
202 pub fn file_format_read_version(&self) -> &FileFormat {
203 &self.file_format_read_version
204 }
205
206 pub fn file_format_write_version(&self) -> &FileFormat {
207 &self.file_format_write_version
208 }
209
210 /// SQLite has the ability to set aside a small number of extra bytes at the end of
211 /// every page for use by extensions. These extra bytes are used, for example, by
212 /// the SQLite Encryption Extension to store a nonce and/or cryptographic checksum
213 /// associated with each page. The "reserved space" size in the 1-byte integer at
214 /// offset 20 is the number of bytes of space at the end of each page to reserve
215 /// for extensions. This value is usually 0. The value can be odd.
216
217 /// The "usable size" of a database page is the page size specified by the 2-byte
218 /// integer at offset 16 in the header less the "reserved" space size recorded in
219 /// the 1-byte integer at offset 20 in the header. The usable size of a page might
220 /// be an odd number. However, the usable size is not allowed to be less than 480.
221 /// In other words, if the page size is 512, then the reserved space size cannot
222 /// exceed 32.
223 pub fn reserved_bytes_per_page(&self) -> u8 {
224 self.reserved_bytes_per_page
225 }
226
227 pub fn payload_fraction(&self) -> &Payload {
228 &self.payload_fraction
229 }
230
231 /// The file change counter is a 4-byte big-endian integer at offset 24 that is
232 /// incremented whenever the database file is unlocked after having been modified.
233 /// When two or more processes are reading the same database file, each process can
234 /// detect database changes from other processes by monitoring the change counter.
235 /// A process will normally want to flush its database page cache when another
236 /// process modified the database, since the cache has become stale. The file
237 /// change counter facilitates this.
238
239 /// In WAL mode, changes to the database are detected using the wal-index and so
240 /// the change counter is not needed. Hence, the change counter might not be
241 /// incremented on each transaction in WAL mode.
242 pub fn file_change_counter(&self) -> u32 {
243 self.file_change_counter
244 }
245
246 /// The 4-byte big-endian integer at offset 28 into the header stores the size of
247 /// the database file in pages. If this in-header datasize size is not valid (see
248 /// the next paragraph), then the database size is computed by looking at the
249 /// actual size of the database file. Older versions of SQLite ignored the
250 /// in-header database size and used the actual file size exclusively. Newer
251 /// versions of SQLite use the in-header database size if it is available but fall
252 /// back to the actual file size if the in-header database size is not valid.
253
254 /// The in-header database size is only considered to be valid if it is non-zero
255 /// and if the 4-byte change counter at offset 24 exactly matches the 4-byte
256 /// version-valid-for number at offset 92. The in-header database size is always
257 /// valid when the database is only modified using recent versions of SQLite,
258 /// versions 3.7.0 (2010-07-21) and later. If a legacy version of SQLite writes to
259 /// the database, it will not know to update the in-header database size and so the
260 /// in-header database size could be incorrect. But legacy versions of SQLite will
261 /// also leave the version-valid-for number at offset 92 unchanged so it will not
262 /// match the change-counter. Hence, invalid in-header database sizes can be
263 /// detected (and ignored) by observing when the change-counter does not match the
264 /// version-valid-for number.
265 pub fn in_header_database_size(&self) -> u32 {
266 self.in_header_database_size
267 }
268
269 pub fn freelist(&self) -> &Freelist {
270 &self.freelist
271 }
272
273 pub fn schema(&self) -> &Schema {
274 &self.schema
275 }
276
277 /// The 4-byte big-endian signed integer at offset 48 is the suggested cache size
278 /// in pages for the database file. The value is a suggestion only and SQLite is
279 /// under no obligation to honor it. The absolute value of the integer is used as
280 /// the suggested size. The suggested cache size can be set using the
281 /// default_cache_size pragma.
282 pub fn default_page_cache_size(&self) -> u32 {
283 self.default_page_cache_size
284 }
285
286 pub fn database_text_encoding(&self) -> &DatabaseTextEncoding {
287 &self.database_text_encoding
288 }
289
290 /// The 4-byte big-endian integer at offset 60 is the user version which is set and
291 /// queried by the user_version pragma. The user version is not used by SQLite.
292 pub fn user_version(&self) -> u32 {
293 self.user_version
294 }
295
296 pub fn vacuum(&self) -> Option<&Vacuum> {
297 if let Some(vac) = &self.vacuum {
298 Some(vac)
299 } else {
300 None
301 }
302 }
303
304 /// The 4-byte big-endian integer at offset 68 is an "Application ID" that can be
305 /// set by the PRAGMA application_id command in order to identify the database as
306 /// belonging to or associated with a particular application. The application ID is
307 /// intended for database files used as an application file-format. The application
308 /// ID can be used by utilities such as file(1) to determine the specific file type
309 /// rather than just reporting "SQLite3 Database". A list of assigned application
310 /// IDs can be seen by consulting the magic.txt file in the SQLite source repository.
311 pub fn application_id(&self) -> u32 {
312 self.application_id
313 }
314
315 pub fn last_update(&self) -> &LastUpdate {
316 &self.last_update
317 }
318}