#![cfg_attr(
any(debug_assertions, not(feature = "unsafe-optim")),
forbid(unsafe_code)
)]
#![deny(missing_docs)]
pub use page_store::{
AccessPagedData, BlockPageStg, HashMap, HashSet, Limits, PageStorage, PageStorageInfo, SaveOp,
SharedPagedData,
};
pub use crate::builtin::standard_builtins;
pub use atom_file::{
AtomicFile, BasicAtomicFile, BasicStorage, DummyFile, FastFileStorage, MemFile,
MultiFileStorage, SimpleFileStorage, Storage,
};
#[cfg(feature = "gentrans")]
pub use crate::gentrans::{GenQuery, GenTransaction, Part};
#[cfg(feature = "builtin")]
pub use crate::{
builtin::check_types,
compile::{c_bool, c_float, c_int, c_value},
exec::EvalEnv,
expr::ObjRef,
expr::{Block, DataKind, Expr},
run::{CExp, CExpPtr, CompileFunc},
value::Value,
};
#[cfg(not(feature = "builtin"))]
use crate::{
compile::{c_bool, c_int, c_value},
exec::EvalEnv,
expr::{Block, DataKind, Expr},
run::{CExp, CExpPtr, CompileFunc},
value::Value,
};
use crate::{
alloc::*,
bytes::ByteStorage,
expr::*,
page::{Page, PagePtr},
parse::Parser,
run::*,
sortedfile::{Asc, Id, Record, SortedFile},
table::{ColInfo, IndexInfo, Row, Table},
util::{SmallSet, nd},
value::*,
};
#[cfg(feature = "pstd")]
pub use pstd::collections::BTreeMap;
#[cfg(not(feature = "pstd"))]
pub use std::collections::BTreeMap;
use std::{
any::Any,
cell::{Cell, RefCell},
cmp::Ordering,
collections::BTreeSet,
panic,
rc::Rc,
sync::Arc,
};
#[cfg(feature = "max")]
#[macro_use]
pub mod util;
#[cfg(not(feature = "max"))]
#[macro_use]
mod util;
#[cfg(feature = "gentrans")]
pub mod gentrans;
pub mod test;
pub mod bench;
pub mod alloc;
#[cfg(feature = "builtin")]
pub mod builtin;
#[cfg(not(feature = "builtin"))]
mod builtin;
#[cfg(feature = "builtin")]
pub mod compile;
#[cfg(not(feature = "builtin"))]
mod compile;
#[cfg(feature = "builtin")]
pub mod expr;
#[cfg(not(feature = "builtin"))]
mod expr;
#[cfg(feature = "table")]
pub mod sortedfile;
#[cfg(not(feature = "table"))]
mod sortedfile;
#[cfg(feature = "table")]
pub mod table;
#[cfg(not(feature = "table"))]
mod table;
#[cfg(feature = "table")]
pub mod page;
#[cfg(not(feature = "table"))]
mod page;
#[cfg(feature = "builtin")]
pub mod value;
#[cfg(not(feature = "builtin"))]
mod value;
#[cfg(feature = "max")]
pub mod exec;
#[cfg(not(feature = "max"))]
mod exec;
pub mod compact;
#[cfg(feature = "max")]
pub mod sys;
#[cfg(not(feature = "max"))]
mod sys;
#[cfg(feature = "max")]
pub mod parse;
#[cfg(not(feature = "max"))]
mod parse;
#[cfg(feature = "max")]
pub mod run;
#[cfg(not(feature = "max"))]
mod run;
#[cfg(feature = "max")]
pub mod cexp;
#[cfg(not(feature = "max"))]
mod cexp;
#[cfg(feature = "max")]
pub mod bytes;
#[cfg(not(feature = "max"))]
mod bytes;
pub type Data = Arc<Vec<u8>>;
pub struct MData(Data);
impl MData {
pub fn new(data: Data) -> MData {
MData(data)
}
pub fn to_data(&mut self) -> Data {
self.0.clone()
}
}
impl std::ops::Deref for MData {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for MData {
fn deref_mut(&mut self) -> &mut Self::Target {
Arc::make_mut(&mut self.0)
}
}
#[derive(Clone)]
pub struct DB(pub LRc<Database>);
impl DB {
pub fn run(&self, source: &str, tr: &mut dyn Transaction) {
if let Some(e) = self.go(source, tr) {
let err = format!(
"{} in {} at line {} column {}.",
e.msg, e.rname, e.line, e.column
);
tr.set_error(&err);
self.0.err.set(true);
}
}
fn go(&self, source: &str, tr: &mut dyn Transaction) -> Option<SqlError> {
let mut p = Parser::new(source, self);
let result = std::panic::catch_unwind(panic::AssertUnwindSafe(|| {
p.batch(tr);
}));
if let Err(x) = result {
Some(if let Some(e) = x.downcast_ref::<SqlError>() {
SqlError {
msg: e.msg.clone(),
line: e.line,
column: e.column,
rname: e.rname.clone(),
}
} else if let Some(s) = x.downcast_ref::<&str>() {
p.make_error((*s).to_string())
} else if let Some(s) = x.downcast_ref::<String>() {
p.make_error(s.to_string())
} else {
p.make_error("unrecognised/unexpected error".to_string())
})
} else {
None
}
}
pub fn changed(&self) -> bool {
if self.0.err.get() {
return false;
}
for bs in &self.0.bs {
if bs.changed() {
return true;
}
}
for t in self.0.tables.borrow().values() {
if t.id_gen_dirty.get() {
return true;
}
if t.file.changed() {
return true;
}
}
false
}
pub fn set_stash_mem_limit(&self, to: usize) {
self.0.apd.spd.stash.lock().unwrap().mem_limit = to;
}
pub fn function_update(&self) -> bool {
self.0.function_reset.get()
}
#[cfg(not(feature = "table"))]
fn get_table(&self, name: &ObjRef) -> Option<LRc<Table>> {
if let Some(t) = self.0.tables.borrow().get(name) {
return Some(t.clone());
}
sys::get_table(self, name)
}
#[cfg(feature = "table")]
pub fn get_table(&self, name: &ObjRef) -> Option<LRc<Table>> {
if let Some(t) = self.0.tables.borrow().get(name) {
return Some(t.clone());
}
sys::get_table(self, name)
}
#[cfg(feature = "table")]
pub fn table(&self, schema: &str, name: &str) -> LRc<Table> {
self.get_table(&ObjRef::new(schema, name)).unwrap()
}
fn get_function(&self, name: &ObjRef) -> Option<LRc<Function>> {
if let Some(f) = self.0.functions.borrow().get(name) {
return Some(f.clone());
}
sys::get_function(self, name)
}
fn publish_table(&self, table: LRc<Table>) {
let name = table.info.name.clone();
self.0.tables.borrow_mut().insert(name, table);
}
fn encode(&self, val: &Value, size: usize) -> Code {
let bytes: &[u8] = match val {
Value::RcBinary(x) => x,
Value::ArcBinary(x) => x,
Value::String(x) => x.as_bytes(),
_ => {
return Code {
id: u64::MAX,
ft: 0,
};
}
};
if bytes.len() < size {
return Code {
id: u64::MAX,
ft: 0,
};
}
let tbe = &bytes[size - 9..];
let ft = bytes::fragment_type(tbe.len(), &self.0.bpf);
let id = self.0.bs[ft].encode(self, &bytes[size - 9..]);
Code { id, ft }
}
fn decode(&self, code: Code, inline: usize) -> LVec<u8> {
self.0.bs[code.ft].decode(self, code.id, inline)
}
fn delcode(&self, code: Code) {
if code.id != u64::MAX {
self.0.bs[code.ft].delcode(self, code.id);
}
}
fn alloc_page(&self) -> u64 {
self.0.apd.alloc_page()
}
fn free_page(&self, lpnum: u64) {
self.0.apd.free_page(lpnum);
}
#[cfg(feature = "pack")]
fn lp_size(&self, pnum: u64) -> u64 {
self.0.apd.spd.ps.read().unwrap().size(pnum) as u64
}
pub fn save(&self) -> usize {
let op = if self.0.err.get() {
self.0.err.set(false);
SaveOp::RollBack
} else {
SaveOp::Save
};
for bs in &self.0.bs {
bs.save(self, op);
}
let tm = &*self.0.tables.borrow();
for t in tm.values() {
if t.id_gen_dirty.get() {
if op == SaveOp::Save {
sys::save_id_gen(self, t.id as u64, t.id_gen.get().unwrap());
} else {
t.id_gen.set(None);
}
t.id_gen_dirty.set(false);
}
}
for t in tm.values() {
t.save(self, op);
}
if self.0.function_reset.get() {
for function in self.0.functions.borrow().values() {
function.ilist.borrow_mut().clear();
}
self.0.functions.borrow_mut().clear();
self.0.function_reset.set(false);
}
self.0.apd.save(op)
}
#[cfg(feature = "pack")]
fn repack_file(&self, k: i64, schema: &str, tname: &str) -> i64 {
if k >= 0 {
let name = ObjRef::new(schema, tname);
if let Some(t) = self.get_table(&name) {
return t.repack(self, k as usize);
}
} else {
let k = (-k - 1) as usize;
if k < 4 {
return self.0.bs[k].repack_file(self);
}
}
-1
}
#[cfg(feature = "renumber")]
pub fn renumber(&self) {
let target = self.0.apd.spd.ps.write().unwrap().load_free_pages();
if let Some(target) = target {
for bs in &self.0.bs {
bs.file.renumber(self, target);
}
for t in self.0.tables.borrow().values() {
let tf = &t.file;
let mut root_page = tf.root_page.get();
if root_page >= target {
root_page = tf.ren(root_page, self);
tf.root_page.set(root_page);
sys::set_root(self, t.id, root_page);
}
tf.renumber(self, target);
for ix in &mut *t.ixlist.borrow_mut() {
let mut root_page = ix.file.root_page.get();
if root_page >= target {
root_page = ix.file.ren(root_page, self);
ix.file.root_page.set(root_page);
sys::set_ix_root(self, ix.id, root_page);
}
ix.file.renumber(self, target);
}
}
self.0.apd.spd.ps.write().unwrap().set_alloc_pn(target);
}
}
#[cfg(feature = "verify")]
pub fn verify(&self) -> LString {
let (mut pages, total) = self.0.apd.spd.ps.write().unwrap().get_free();
let total = total as usize;
let free = pages.len();
for bs in &self.0.bs {
bs.file.get_used(self, &mut pages);
}
for t in self.0.tables.borrow().values() {
t.get_used(self, &mut pages);
}
assert_eq!(pages.len(), total);
let mut result = LString::new();
use std::fmt::Write;
write!(
result,
"Logical page summary: free={} used={} total={}",
free,
total - free,
total
)
.unwrap();
result
}
}
pub type BuiltinMap = HashMap<Box<str>, (DataKind, CompileFunc)>;
pub struct Database {
pub apd: AccessPagedData,
pub builtins: Arc<BuiltinMap>,
pub sys_schema: LRc<Table>,
pub sys_table: LRc<Table>,
pub sys_column: LRc<Table>,
pub sys_index: LRc<Table>,
pub sys_index_col: LRc<Table>,
pub sys_function: LRc<Table>,
pub schemas: RefCell<LHashMap<String, i64>>,
pub tables: RefCell<LHashMap<ObjRef, LRc<Table>>>,
pub functions: RefCell<LHashMap<ObjRef, LRc<Function>>>,
pub lastid: Cell<i64>,
pub err: Cell<bool>,
pub is_new: bool,
bs: LVec<ByteStorage>,
pub function_reset: Cell<bool>,
page_size_max: usize,
bpf: [usize; bytes::NFT],
}
const SYS_ROOT_LAST: u64 = 16;
impl Database {
#[allow(clippy::new_ret_no_self)]
pub fn new(apd: AccessPagedData, initsql: &str, builtins: Arc<BuiltinMap>) -> DB {
let is_new = apd.is_new();
let mut tb = TableBuilder::new(5);
let sys_schema = tb.nt("Schema", &[("Name", STRING)]);
let sys_table = tb.nt(
"Table",
&[
("Root", INT),
("Schema", INT),
("Name", STRING),
("IdGen", INT),
],
);
let sys_column = tb.nt("Column", &[("Table", INT), ("Name", STRING), ("Type", INT)]);
let sys_index = tb.nt("Index", &[("Root", INT), ("Table", INT), ("Name", STRING)]);
let sys_index_col = tb.nt("IndexColumn", &[("Index", INT), ("ColId", INT)]);
let sys_function = tb.nt(
"Function",
&[("Schema", INT), ("Name", NAMESTR), ("Def", BIGSTR)],
);
sys_schema.add_index0(tb.rt(), &[0], 1);
sys_table.add_index0(tb.rt(), &[1, 2], 2);
sys_column.add_index0(tb.rt(), &[0], 3);
sys_index.add_index0(tb.rt(), &[1], 4);
sys_index_col.add_index0(tb.rt(), &[0], 5);
sys_function.add_index0(tb.rt(), &[0, 1], 6);
sys_function.add_index0(tb.rt(), &[1], 7);
let page_size_max = apd.spd.psi.max_size_page();
let bpf = bytes::bpf(apd.spd.psi.half_size_page());
let mut bs = LVec::with_capacity(bpf.len());
for (ft, bpf) in bpf.iter().enumerate() {
bs.push(ByteStorage::new(ft as u64, *bpf));
}
let db = DB(LRc::new(Database {
apd,
sys_schema,
sys_table,
sys_column,
sys_index,
sys_index_col,
sys_function,
bs,
schemas: RefCell::new(lhashmap()),
functions: RefCell::new(lhashmap()),
tables: RefCell::new(lhashmap()),
builtins,
function_reset: Cell::new(false),
lastid: Cell::new(0),
err: Cell::new(false),
is_new,
page_size_max,
bpf,
}));
assert!(tb.alloc as u64 - 1 == SYS_ROOT_LAST);
if is_new {
for _ft in 0..bytes::NFT {
db.alloc_page(); }
}
for t in &tb.list {
if !is_new {
t.id_gen.set(None);
}
db.publish_table(t.clone());
}
if is_new {
let sysinit = "
CREATE SCHEMA sys
GO
CREATE TABLE sys.Schema( Name string )
CREATE TABLE sys.Table( Root int, Schema int, Name string, IdGen int )
CREATE TABLE sys.Column( Table int, Name string, Type int )
CREATE TABLE sys.Index( Root int, Table int, Name string )
CREATE TABLE sys.IndexColumn( Index int, ColId int )
CREATE TABLE sys.Function( Schema int, Name string(31), Def string(249) )
GO
CREATE INDEX ByName ON sys.Schema(Name)
CREATE INDEX BySchemaName ON sys.Table(Schema,Name)
CREATE INDEX ByTable ON sys.Column(Table)
CREATE INDEX ByTable ON sys.Index(Table)
CREATE INDEX ByIndex ON sys.IndexColumn(Index)
CREATE INDEX BySchemaName ON sys.Function(Schema,Name)
CREATE INDEX ByName ON sys.Function(Name)
GO
";
let mut dq = DummyTransaction {};
db.run(sysinit, &mut dq);
db.run(initsql, &mut dq);
db.save();
}
db
}
}
impl Drop for Database {
fn drop(&mut self) {
for function in self.functions.borrow().values() {
function.ilist.borrow_mut().clear();
}
}
}
struct TableBuilder {
alloc: usize,
list: TVec<LRc<Table>>,
}
impl TableBuilder {
fn new(n: usize) -> Self {
Self {
alloc: bytes::NFT,
list: TVec::with_capacity_in(n, temp()),
}
}
fn nt(&mut self, name: &str, ct: &[(&str, DataType)]) -> LRc<Table> {
let root = self.rt();
let id = 1 + (root - bytes::NFT as u64);
let name = ObjRef::new("sys", name);
let info = ColInfo::new(name, ct);
let table = Table::new(id as i64, root, 1, LRc::new(info));
self.list.push(table.clone());
table
}
fn rt(&mut self) -> u64 {
let result = self.alloc;
self.alloc += 1;
result as u64
}
}
pub trait Transaction: Any {
fn status_code(&mut self, _code: i64) {}
fn header(&mut self, _name: &str, _value: &str) {}
fn selected(&mut self, values: &[Value]);
fn global(&self, _kind: i64) -> i64 {
0
}
fn arg(&mut self, _kind: i64, _name: &str) -> LRc<LString> {
LRc::new(LString::new())
}
fn file_attr(&mut self, _fnum: i64, _atx: i64) -> Rc<String> {
Rc::new(String::new())
}
fn file_content(&mut self, _fnum: i64) -> Arc<Vec<u8>> {
nd()
}
fn set_error(&mut self, err: &str);
fn get_error(&mut self) -> String {
String::new()
}
fn set_extension(&mut self, _ext: Box<dyn Any + Send + Sync>) {}
fn get_extension(&mut self) -> Box<dyn Any + Send + Sync> {
Box::new(())
}
}
struct DummyTransaction {}
impl Transaction for DummyTransaction {
fn selected(&mut self, _values: &[Value]) {}
fn set_error(&mut self, err: &str) {
println!("Error: {}", err);
}
}