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