Skip to main content

rustdb/
lib.rs

1//! This crate (rustdb) implements a high-performance database written entirely in [Rust](https://www.rust-lang.org/).
2//!
3//! The SQL-like language is relatively minimal, and does not (currently) include features such as joins or views.
4//! Instead it has high performance SET .. FROM ... and FOR .. FROM statements to access database tables,
5//! generally using an INDEX.
6//!
7//! Read-only transactions run immediately and concurrently on a virtual read-only copy of the database, and cannot be blocked.
8//! Write transactions run sequentially (and should typically execute in around 100 micro-seconds). The [Storage] trait allows a variety of underlying storage, including [SimpleFileStorage], [MemFile] and [AtomicFile].
9//!
10//! Transactions that modify the database can be logged, which allows for database replication.
11
12//!# Interface
13//!
14//! The method [Database::run] is called to execute an SQL query.
15//! This takes a [Transaction] parameter which accumulates SELECT results and which also has methods
16//! for accessing input parameters and controlling output. Custom builtin functions implement [CExp]
17//! and have access to the transaction via an EvalEnv parameter, which can be downcast if necessary.
18//!
19//! It is also possible to access the table data directly, see email_loop in example program.   
20//!
21//!# Example
22//! [See here](https://crates.io/crates/rustweb2) for an example program -
23//! a webserver, with timed jobs, password hashing, data compression, email transmission and database replication.
24//! Also has a Manual for the SQL-like language, user interface for database browsing/editing etc.
25//!
26//!# Features
27//!
28//! This crate supports the following cargo features:
29//! - `gentrans` : enables [gentrans] module ( sample implementation of [Transaction] ).
30//! - `serde` : enables serialisation of [GenQuery] via serde crate.
31//! - `builtin` : Allows extra SQL builtin functions to be defined.
32//! - `table` : Allow direct access to database tables.
33//! - `max` : maximal interface, including internal modules (which may not be stable).
34//! - `verify` : Allows database structure to be verified using builtin function VERIFYDB.
35//! - `pack` : Allows database pages to be packed using builtin function REPACKFILE.
36//! - `renumber` : Allows database pages to be renumbered using builtin function RENUMBER, eliminating free pages.
37//! - `unsafe-optim` : Enable unsafe optimisations in release mode.
38//! - `log` : Log "interesting" information about database operation (helps give an idea what is happening).
39//! - `compact` : Default page storage is CompactFile rather than [BlockPageStg] (can be set explicitly using [pstore::SharedPagedData::new_from_ps] ).
40//!
41//! By default, all features except serde, unsafe-optim, log and compact are enabled.
42//!
43//!# General Design of Database
44//!
45//! SortedFile stores fixed size Records in a tree of Pages.
46//! SortedFile is used to implement:
47//!
48//! - Database Table storage. Each fixed size record has a 64-bit Id.
49//!
50//! - Variable length values which are split into fragments, although up to 249 bytes can be stored in the fixed size record.
51//!
52//! - Index storage - an index record refers back to the main table using the 64-bit Id.
53//!
54//! When a page becomes too big, it is split into two pages.
55//!
56//! Each page is implemented as a binary tree ( so there is a tree of trees ).
57//!
58//! [SharedPagedData] allows logical database pages to be shared to allow concurrent readers.
59//!
60//! [AtomicFile] ensures that database updates are all or nothing.
61//!
62//! The hierarchy overall: [Table] -> [SortedFile] -> [SharedPagedData] -> [PageStorage] -> [AtomicFile] -> [Storage].
63//!
64//!# Test example
65//!
66//! ```
67//!     use rustdb::*;
68//!     use std::sync::Arc;
69//!     let stg = MemFile::new();
70//!
71//!     let spd = SharedPagedData::new(stg);
72//!     let wapd = AccessPagedData::new_writer(spd);
73//!
74//!     let mut bmap = BuiltinMap::default();
75//!     standard_builtins(&mut bmap);
76//!     let bmap = Arc::new(bmap);
77//!
78//!     let db = Database::new(wapd, "", bmap);
79//!     let mut tr = GenTransaction::default();
80//!     let sql = "
81//! CREATE SCHEMA test GO
82//! CREATE TABLE test.Cust(Name string) GO
83//! INSERT INTO test.Cust(Name) VALUES ('freddy')
84//! SELECT Name FROM test.Cust
85//! ";
86//!     db.run(&sql, &mut tr);
87//!     assert!( db.changed() );
88//!     assert!( db.save() > 0 );
89//!     assert!( tr.rp.output == b"freddy" );
90//! ```
91
92#![cfg_attr(
93    any(debug_assertions, not(feature = "unsafe-optim")),
94    forbid(unsafe_code)
95)] // see util::perf_assert! macro
96#![deny(missing_docs)]
97
98pub use crate::{
99    blockpagestg::BlockPageStg,
100    builtin::standard_builtins,
101    pstore::{AccessPagedData, SharedPagedData},
102    stg::{PageStorage, PageStorageInfo, SimpleFileStorage},
103};
104pub use atom_file::{AtomicFile, BasicAtomicFile, DummyFile, MemFile, MultiFileStorage, Storage};
105
106#[cfg(feature = "gentrans")]
107pub use crate::gentrans::{GenQuery, GenTransaction, Part};
108
109#[cfg(feature = "builtin")]
110pub use crate::{
111    builtin::check_types,
112    compile::{c_bool, c_float, c_int, c_value},
113    exec::EvalEnv,
114    expr::ObjRef,
115    expr::{Block, DataKind, Expr},
116    run::{CExp, CExpPtr, CompileFunc},
117    value::Value,
118};
119#[cfg(not(feature = "builtin"))]
120use crate::{
121    compile::{c_bool, c_int, c_value},
122    exec::EvalEnv,
123    expr::{Block, DataKind, Expr},
124    run::{CExp, CExpPtr, CompileFunc},
125    value::Value,
126};
127
128use crate::{
129    bytes::ByteStorage,
130    expr::*,
131    page::{Page, PagePtr},
132    parse::Parser,
133    run::*,
134    sortedfile::{Asc, Id, Record, SortedFile},
135    table::{ColInfo, IndexInfo, Row, SaveOp, Table},
136    util::{SmallSet, nd, newmap},
137    value::*,
138};
139
140#[cfg(feature = "pstd")]
141pub use pstd::collections::BTreeMap;
142
143#[cfg(not(feature = "pstd"))]
144pub use std::collections::BTreeMap;
145
146use std::{
147    any::Any,
148    cell::{Cell, RefCell},
149    cmp::Ordering,
150    collections::BTreeSet,
151    panic,
152    rc::Rc,
153    sync::{Arc, Mutex, RwLock},
154};
155
156// use std::collections::{HashMap,HashSet};
157use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
158
159/// Utility functions and macros, [SmallSet].
160#[cfg(feature = "max")]
161#[macro_use]
162pub mod util;
163#[cfg(not(feature = "max"))]
164#[macro_use]
165mod util;
166
167#[cfg(feature = "gentrans")]
168/// [GenTransaction] ( implementation of [Transaction] ).
169pub mod gentrans;
170
171/// Backing [Storage] for database. See also [AtomicFile].
172pub mod stg;
173
174/// [block::BlockStg] - divides [Storage] into relocatable fixed size blocks, basis for [dividedstg::DividedStg].
175pub mod block;
176
177/// [dividedstg::DividedStg] divides [Storage] into multiple sub-files of arbitrary size.
178pub mod dividedstg;
179
180/// [BlockPageStg] - implementation of [PageStorage] trait based on [dividedstg::DividedStg].
181pub mod blockpagestg;
182
183/// Page storage and sharing, [SharedPagedData] and [AccessPagedData].
184pub mod pstore;
185
186/// Test module.
187pub mod test;
188
189/// Benchmark - compare RustDb with competitors!
190pub mod bench;
191
192// Conditional modules.
193
194// #[cfg(target_os = "windows")]
195// Optimised implementatation of [Storage] (windows only).
196// This didn't work out - actually ran slower!
197// pub mod stgwin;
198
199#[cfg(feature = "builtin")]
200/// Compilation of builtin functions, [standard_builtins].
201pub mod builtin;
202#[cfg(not(feature = "builtin"))]
203mod builtin;
204
205#[cfg(feature = "builtin")]
206/// Functions to compile parsed expressions, checking types.
207pub mod compile;
208#[cfg(not(feature = "builtin"))]
209mod compile;
210
211#[cfg(feature = "builtin")]
212/// Expression types, result of parsing. [Expr], [DataKind], [ObjRef], [Block].
213pub mod expr;
214#[cfg(not(feature = "builtin"))]
215mod expr;
216
217#[cfg(feature = "table")]
218/// [SortedFile] : [Record] storage.
219pub mod sortedfile;
220#[cfg(not(feature = "table"))]
221mod sortedfile;
222
223#[cfg(feature = "table")]
224/// [Table], [ColInfo], [Row] and other Table types for direct table access.
225pub mod table;
226#[cfg(not(feature = "table"))]
227mod table;
228
229#[cfg(feature = "table")]
230/// [Page] of records for [SortedFile].
231pub mod page;
232#[cfg(not(feature = "table"))]
233mod page;
234
235#[cfg(feature = "builtin")]
236/// Run-time [Value].
237pub mod value;
238#[cfg(not(feature = "builtin"))]
239mod value;
240
241#[cfg(feature = "max")]
242/// [EvalEnv] : [Instruction] execution.
243pub mod exec;
244#[cfg(not(feature = "max"))]
245mod exec;
246
247/// [compact::CompactFile] :  alternative implementation of [PageStorage] trait.
248pub mod compact;
249
250#[cfg(feature = "max")]
251/// System table functions.
252pub mod sys;
253#[cfg(not(feature = "max"))]
254mod sys;
255
256#[cfg(feature = "max")]
257/// [Parser].
258pub mod parse;
259#[cfg(not(feature = "max"))]
260mod parse;
261
262#[cfg(feature = "max")]
263/// [Instruction] and other run time types.
264pub mod run;
265#[cfg(not(feature = "max"))]
266mod run;
267
268#[cfg(feature = "max")]
269/// Structs that implement [CExp] trait.
270pub mod cexp;
271#[cfg(not(feature = "max"))]
272mod cexp;
273
274#[cfg(feature = "max")]
275/// Heap with keys that can be modified for tracking least used page.
276pub mod heap;
277#[cfg(not(feature = "max"))]
278mod heap;
279
280#[cfg(feature = "max")]
281/// Storage of variable length values : [ByteStorage].
282pub mod bytes;
283#[cfg(not(feature = "max"))]
284mod bytes;
285
286// End of modules.
287
288/// ```Arc<Vec<u8>>```
289pub type Data = Arc<Vec<u8>>;
290
291/// Mutable Data, copied on write.
292pub struct MData(Data);
293
294impl MData {
295    /// New MData from Data.
296    pub fn new(data: Data) -> MData {
297        MData(data)
298    }
299    /// Data from MData.
300    pub fn to_data(&mut self) -> Data {
301        self.0.clone()
302    }
303}
304
305impl std::ops::Deref for MData {
306    type Target = Vec<u8>;
307    fn deref(&self) -> &Self::Target {
308        &self.0
309    }
310}
311
312impl std::ops::DerefMut for MData {
313    fn deref_mut(&mut self) -> &mut Self::Target {
314        Arc::make_mut(&mut self.0)
315    }
316}
317
318/// ```Rc<Database>```
319pub type DB = Rc<Database>;
320
321/// Map that defines SQL pre-defined functions.
322pub type BuiltinMap = HashMap<String, (DataKind, CompileFunc)>;
323
324/// Database with SQL-like interface.
325pub struct Database {
326    /// Page storage.
327    pub apd: AccessPagedData,
328
329    /// Defined builtin functions.
330    pub builtins: Arc<BuiltinMap>,
331
332    // System tables.
333    /// Schema table.
334    pub sys_schema: Rc<Table>,
335    /// Table table.
336    pub sys_table: Rc<Table>,
337    /// Column definitions table.
338    pub sys_column: Rc<Table>,
339    /// Index table.
340    pub sys_index: Rc<Table>,
341    /// Index column definitions.
342    pub sys_index_col: Rc<Table>,
343    /// Function (FN) definitions.
344    pub sys_function: Rc<Table>,
345
346    /// Cache of loaded Schemas.
347    pub schemas: RefCell<HashMap<String, i64>>,
348    /// Cache of loaded Tables.
349    pub tables: RefCell<HashMap<ObjRef, Rc<Table>>>,
350    /// Cache of loaded Functions.
351    pub functions: RefCell<HashMap<ObjRef, Rc<Function>>>,
352
353    /// Last id generated by INSERT.
354    pub lastid: Cell<i64>,
355    /// Has there been an error since last save?
356    pub err: Cell<bool>,
357    /// Is the database new?
358    pub is_new: bool,
359
360    /// Storage of variable length data.
361    bs: Vec<ByteStorage>,
362    /// Flag to reset the functions cache after save.
363    pub function_reset: Cell<bool>,
364    /// Maximum size of logical page.
365    page_size_max: usize,
366
367    bpf: [usize; bytes::NFT],
368}
369
370const SYS_ROOT_LAST: u64 = 16;
371
372impl Database {
373    /// Construct a new DB, based on the specified file.
374    /// initsql is used to initialise a new database.
375    /// builtins specifies the functions callable in SQL code such as SUBSTR, REPLACE etc.
376    pub fn new(apd: AccessPagedData, initsql: &str, builtins: Arc<BuiltinMap>) -> DB {
377        let is_new = apd.is_new();
378        let mut tb = TableBuilder::new();
379        let sys_schema = tb.nt("Schema", &[("Name", STRING)]);
380        let sys_table = tb.nt(
381            "Table",
382            &[
383                ("Root", INT),
384                ("Schema", INT),
385                ("Name", STRING),
386                ("IdGen", INT),
387            ],
388        );
389        let sys_column = tb.nt("Column", &[("Table", INT), ("Name", STRING), ("Type", INT)]);
390        let sys_index = tb.nt("Index", &[("Root", INT), ("Table", INT), ("Name", STRING)]);
391        let sys_index_col = tb.nt("IndexColumn", &[("Index", INT), ("ColId", INT)]);
392        let sys_function = tb.nt(
393            "Function",
394            &[("Schema", INT), ("Name", NAMESTR), ("Def", BIGSTR)],
395        );
396        sys_schema.add_index(tb.rt(), vec![0], 1);
397        sys_table.add_index(tb.rt(), vec![1, 2], 2);
398        sys_column.add_index(tb.rt(), vec![0], 3);
399        sys_index.add_index(tb.rt(), vec![1], 4);
400        sys_index_col.add_index(tb.rt(), vec![0], 5);
401        sys_function.add_index(tb.rt(), vec![0, 1], 6);
402        sys_function.add_index(tb.rt(), vec![1], 7);
403
404        let page_size_max = apd.spd.psi.max_size_page();
405
406        let bpf = bytes::bpf(apd.spd.psi.half_size_page());
407
408        let mut bs = Vec::new();
409        for (ft, bpf) in bpf.iter().enumerate() {
410            bs.push(ByteStorage::new(ft as u64, *bpf));
411        }
412
413        let db = Rc::new(Database {
414            apd,
415            sys_schema,
416            sys_table,
417            sys_column,
418            sys_index,
419            sys_index_col,
420            sys_function,
421            bs,
422            schemas: newmap(),
423            functions: newmap(),
424            tables: newmap(),
425            builtins,
426            function_reset: Cell::new(false),
427            lastid: Cell::new(0),
428            err: Cell::new(false),
429            is_new,
430            page_size_max,
431            bpf,
432        });
433
434        assert!(tb.alloc as u64 - 1 == SYS_ROOT_LAST);
435
436        if is_new {
437            for _ft in 0..bytes::NFT {
438                db.alloc_page(); // Allocate page for byte storage.
439            }
440        }
441        for t in &tb.list {
442            if !is_new {
443                t.id_gen.set(None);
444            }
445            db.publish_table(t.clone());
446        }
447
448        if is_new {
449            // The creation order has to match the order above ( so root values are as predicted ).
450            let sysinit = "
451CREATE SCHEMA sys
452GO
453CREATE TABLE sys.Schema( Name string )
454CREATE TABLE sys.Table( Root int, Schema int, Name string, IdGen int )
455CREATE TABLE sys.Column( Table int, Name string, Type int )
456CREATE TABLE sys.Index( Root int, Table int, Name string )
457CREATE TABLE sys.IndexColumn( Index int, ColId int )
458CREATE TABLE sys.Function( Schema int, Name string(31), Def string(249) )
459GO
460CREATE INDEX ByName ON sys.Schema(Name)
461CREATE INDEX BySchemaName ON sys.Table(Schema,Name)
462CREATE INDEX ByTable ON sys.Column(Table)
463CREATE INDEX ByTable ON sys.Index(Table)
464CREATE INDEX ByIndex ON sys.IndexColumn(Index)
465CREATE INDEX BySchemaName ON sys.Function(Schema,Name)
466CREATE INDEX ByName ON sys.Function(Name)
467GO
468";
469            let mut dq = DummyTransaction {};
470            db.run(sysinit, &mut dq);
471            db.run(initsql, &mut dq);
472            db.save();
473        }
474
475        db
476    }
477
478    /// Run a batch of SQL.
479    pub fn run(self: &DB, source: &str, tr: &mut dyn Transaction) {
480        if let Some(e) = self.go(source, tr) {
481            let err = format!(
482                "{} in {} at line {} column {}.",
483                e.msg, e.rname, e.line, e.column
484            );
485            tr.set_error(err);
486            self.err.set(true);
487        }
488    }
489
490    /// Run a batch of SQL.
491    fn go(self: &DB, source: &str, tr: &mut dyn Transaction) -> Option<SqlError> {
492        let mut p = Parser::new(source, self);
493        let result = std::panic::catch_unwind(panic::AssertUnwindSafe(|| {
494            p.batch(tr);
495        }));
496        if let Err(x) = result {
497            Some(if let Some(e) = x.downcast_ref::<SqlError>() {
498                SqlError {
499                    msg: e.msg.clone(),
500                    line: e.line,
501                    column: e.column,
502                    rname: e.rname.clone(),
503                }
504            } else if let Some(s) = x.downcast_ref::<&str>() {
505                p.make_error((*s).to_string())
506            } else if let Some(s) = x.downcast_ref::<String>() {
507                p.make_error(s.to_string())
508            } else {
509                p.make_error("unrecognised/unexpected error".to_string())
510            })
511        } else {
512            None
513        }
514    }
515
516    /// Test whether there are unsaved changes.
517    pub fn changed(self: &DB) -> bool {
518        if self.err.get() {
519            return false;
520        }
521        for bs in &self.bs {
522            if bs.changed() {
523                return true;
524            }
525        }
526        for t in self.tables.borrow().values() {
527            if t.id_gen_dirty.get() {
528                return true;
529            }
530            if t.file.changed() {
531                return true;
532            }
533        }
534        false
535    }
536
537    /// Save updated tables to underlying file ( or rollback if there was an error ).
538    /// Returns the number of logical pages that were updated.
539    pub fn save(self: &DB) -> usize {
540        let op = if self.err.get() {
541            self.err.set(false);
542            SaveOp::RollBack
543        } else {
544            SaveOp::Save
545        };
546        for bs in &self.bs {
547            bs.save(self, op);
548        }
549        let tm = &*self.tables.borrow();
550        for t in tm.values() {
551            if t.id_gen_dirty.get() {
552                if op == SaveOp::Save {
553                    sys::save_id_gen(self, t.id as u64, t.id_gen.get().unwrap());
554                } else {
555                    t.id_gen.set(None);
556                }
557                t.id_gen_dirty.set(false);
558            }
559        }
560        for t in tm.values() {
561            t.save(self, op);
562        }
563        if self.function_reset.get() {
564            for function in self.functions.borrow().values() {
565                function.ilist.borrow_mut().clear();
566            }
567            self.functions.borrow_mut().clear();
568            self.function_reset.set(false);
569        }
570        self.apd.save(op)
571    }
572
573    #[cfg(not(feature = "table"))]
574    /// Get the named table.
575    fn get_table(self: &DB, name: &ObjRef) -> Option<Rc<Table>> {
576        if let Some(t) = self.tables.borrow().get(name) {
577            return Some(t.clone());
578        }
579        sys::get_table(self, name)
580    }
581
582    #[cfg(feature = "table")]
583    /// Get the named table.
584    pub fn get_table(self: &DB, name: &ObjRef) -> Option<Rc<Table>> {
585        if let Some(t) = self.tables.borrow().get(name) {
586            return Some(t.clone());
587        }
588        sys::get_table(self, name)
589    }
590
591    #[cfg(feature = "table")]
592    /// Get the named table ( panics if it does not exist ).
593    pub fn table(self: &DB, schema: &str, name: &str) -> Rc<Table> {
594        self.get_table(&ObjRef::new(schema, name)).unwrap()
595    }
596
597    /// Get the named function.
598    fn get_function(self: &DB, name: &ObjRef) -> Option<Rc<Function>> {
599        if let Some(f) = self.functions.borrow().get(name) {
600            return Some(f.clone());
601        }
602        sys::get_function(self, name)
603    }
604
605    /// Insert the table into the map of tables.
606    fn publish_table(&self, table: Rc<Table>) {
607        let name = table.info.name.clone();
608        self.tables.borrow_mut().insert(name, table);
609    }
610
611    /// Get code for value.
612    fn encode(self: &DB, val: &Value, size: usize) -> Code {
613        let bytes = match val {
614            Value::RcBinary(x) => &**x,
615            Value::ArcBinary(x) => &**x,
616            Value::String(x) => x.as_bytes(),
617            _ => {
618                return Code {
619                    id: u64::MAX,
620                    ft: 0,
621                };
622            }
623        };
624        if bytes.len() < size {
625            return Code {
626                id: u64::MAX,
627                ft: 0,
628            };
629        }
630        let tbe = &bytes[size - 9..];
631        let ft = bytes::fragment_type(tbe.len(), &self.bpf);
632        let id = self.bs[ft].encode(self, &bytes[size - 9..]);
633        Code { id, ft }
634    }
635
636    /// Decode u64 to bytes.
637    fn decode(self: &DB, code: Code, inline: usize) -> Vec<u8> {
638        self.bs[code.ft].decode(self, code.id, inline)
639    }
640
641    /// Delete encoding.
642    fn delcode(self: &DB, code: Code) {
643        if code.id != u64::MAX {
644            self.bs[code.ft].delcode(self, code.id);
645        }
646    }
647
648    /// Allocate a page of underlying file storage.
649    fn alloc_page(self: &DB) -> u64 {
650        self.apd.alloc_page()
651    }
652
653    /// Free a page of underlying file storage.
654    fn free_page(self: &DB, lpnum: u64) {
655        self.apd.free_page(lpnum);
656    }
657
658    #[cfg(feature = "pack")]
659    /// Get size of logical page.
660    fn lp_size(&self, pnum: u64) -> u64 {
661        self.apd.spd.ps.read().unwrap().size(pnum) as u64
662    }
663
664    #[cfg(feature = "pack")]
665    /// Repack the specified sortedfile.
666    fn repack_file(self: &DB, k: i64, schema: &str, tname: &str) -> i64 {
667        if k >= 0 {
668            let name = ObjRef::new(schema, tname);
669            if let Some(t) = self.get_table(&name) {
670                return t.repack(self, k as usize);
671            }
672        } else {
673            let k = (-k - 1) as usize;
674            if k < 4 {
675                return self.bs[k].repack_file(self);
676            }
677        }
678        -1
679    }
680
681    #[cfg(feature = "verify")]
682    /// Verify the page structure of the database.
683    pub fn verify(self: &DB) -> String {
684        let (mut pages, total) = self.apd.spd.ps.write().unwrap().get_free();
685        let total = total as usize;
686
687        let free = pages.len();
688
689        for bs in &self.bs {
690            bs.file.get_used(self, &mut pages);
691        }
692
693        for t in self.tables.borrow().values() {
694            t.get_used(self, &mut pages);
695        }
696
697        assert_eq!(pages.len(), total);
698
699        format!(
700            "Logical page summary: free={} used={} total={}",
701            free,
702            total - free,
703            total
704        )
705    }
706
707    /// Renumber pages.
708    #[cfg(feature = "renumber")]
709    pub fn renumber(self: &DB) {
710        let target = self.apd.spd.ps.write().unwrap().load_free_pages();
711        if let Some(target) = target {
712            for bs in &self.bs {
713                bs.file.renumber(self, target);
714            }
715            for t in self.tables.borrow().values() {
716                let tf = &t.file;
717                let mut root_page = tf.root_page.get();
718                if root_page >= target {
719                    root_page = tf.ren(root_page, self);
720                    tf.root_page.set(root_page);
721                    sys::set_root(self, t.id, root_page);
722                }
723                tf.renumber(self, target);
724                for ix in &mut *t.ixlist.borrow_mut() {
725                    let mut root_page = ix.file.root_page.get();
726                    if root_page >= target {
727                        root_page = ix.file.ren(root_page, self);
728                        ix.file.root_page.set(root_page);
729                        sys::set_ix_root(self, ix.id, root_page);
730                    }
731                    ix.file.renumber(self, target);
732                }
733            }
734            self.apd.spd.ps.write().unwrap().set_alloc_pn(target);
735        }
736    }
737} // end impl Database
738
739impl Drop for Database {
740    /// Clear function instructions to avoid leaking memory.
741    fn drop(&mut self) {
742        for function in self.functions.borrow().values() {
743            function.ilist.borrow_mut().clear();
744        }
745    }
746}
747
748/// For creating system tables.
749struct TableBuilder {
750    alloc: usize,
751    list: Vec<Rc<Table>>,
752}
753impl TableBuilder {
754    fn new() -> Self {
755        Self {
756            alloc: bytes::NFT,
757            list: Vec::new(),
758        }
759    }
760
761    fn nt(&mut self, name: &str, ct: &[(&str, DataType)]) -> Rc<Table> {
762        let root = self.rt();
763        let id = 1 + (root - bytes::NFT as u64);
764        let name = ObjRef::new("sys", name);
765        let info = ColInfo::new(name, ct);
766        let table = Table::new(id as i64, root, 1, Rc::new(info));
767        self.list.push(table.clone());
768        table
769    }
770
771    fn rt(&mut self) -> u64 {
772        let result = self.alloc;
773        self.alloc += 1;
774        result as u64
775    }
776}
777
778/// Input/Output message. Query and Response.
779pub trait Transaction: Any {
780    /// STATUSCODE builtin function. sets the response status code.
781    fn status_code(&mut self, _code: i64) {}
782
783    /// HEADER builtin function, adds header to response.
784    fn header(&mut self, _name: &str, _value: &str) {}
785
786    /// Append SELECT values to response body.
787    fn selected(&mut self, values: &[Value]);
788
789    /// GLOBAL builtin function. Used to get request time.
790    fn global(&self, _kind: i64) -> i64 {
791        0
792    }
793
794    /// ARG builtin function. Get path, query parameter, form value or cookie.
795    fn arg(&mut self, _kind: i64, _name: &str) -> Rc<String> {
796        Rc::new(String::new())
797    }
798
799    /// Get file attribute ( One of name, content_type, file_name )
800    fn file_attr(&mut self, _fnum: i64, _atx: i64) -> Rc<String> {
801        Rc::new(String::new())
802    }
803
804    /// Get file content.
805    fn file_content(&mut self, _fnum: i64) -> Arc<Vec<u8>> {
806        nd()
807    }
808
809    /// Set the error string.
810    fn set_error(&mut self, err: String);
811
812    /// Get the error string.
813    fn get_error(&mut self) -> String {
814        String::new()
815    }
816
817    /// Set the extension.
818    fn set_extension(&mut self, _ext: Box<dyn Any + Send + Sync>) {}
819
820    /// Get the extension. Note: this takes ownership, so extension needs to be set afterwards.
821    fn get_extension(&mut self) -> Box<dyn Any + Send + Sync> {
822        Box::new(())
823    }
824}
825
826/// [Transaction] where output is discarded (used for initialisation ).
827struct DummyTransaction {}
828impl Transaction for DummyTransaction {
829    fn selected(&mut self, _values: &[Value]) {}
830    /// Called if a panic ( error ) occurs.
831    fn set_error(&mut self, err: String) {
832        println!("Error: {}", err);
833    }
834}
835
836/// Memory limits.
837#[non_exhaustive]
838pub struct Limits {
839    /// Atomic file limits
840    pub af_lim: atom_file::Limits,
841    /// Block capacity
842    pub blk_cap: u64,
843    /// Page sizes
844    pub page_sizes: usize,
845    /// Largest division of page
846    pub max_div: usize,
847}
848
849impl Default for Limits {
850    fn default() -> Self {
851        Self {
852            af_lim: atom_file::Limits::default(),
853            blk_cap: 27720,
854            page_sizes: 7,
855            max_div: 12,
856        }
857    }
858}