pub use crate::{
atomfile::AtomicFile,
gentrans::{GenTransaction, Part},
init::INITSQL,
pstore::{AccessPagedData, SharedPagedData},
stg::{MemFile, SimpleFileStorage},
webtrans::WebTransaction,
};
#[cfg(feature = "builtin")]
pub use crate::{
builtin::check_types,
builtin::standard_builtins,
compile::{c_bool, c_float, c_int, c_value},
exec::EvalEnv,
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::{
bytes::ByteStorage,
compact::CompactFile,
expr::*,
page::{Page, PagePtr},
parse::Parser,
run::*,
sortedfile::{Asc, Id, Record, SortedFile},
stg::Storage,
table::{ColInfo, IndexInfo, Row, SaveOp, Table, TablePtr},
util::{nd, newmap, SmallSet},
value::*,
};
use std::{
cell::{Cell, RefCell},
cmp::Ordering,
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
panic,
rc::Rc,
sync::{Arc, Mutex, RwLock},
};
#[cfg(feature = "max")]
#[macro_use]
pub mod util;
#[cfg(not(feature = "max"))]
#[macro_use]
mod util;
pub mod gentrans;
pub mod webtrans;
pub mod init;
pub mod stg;
pub mod pstore;
pub mod atomfile;
#[cfg(feature = "max")]
pub mod builtin;
#[cfg(not(feature = "max"))]
mod builtin;
#[cfg(feature = "max")]
pub mod bytes;
#[cfg(not(feature = "max"))]
mod bytes;
#[cfg(feature = "max")]
pub mod cexp;
#[cfg(not(feature = "max"))]
mod cexp;
#[cfg(feature = "max")]
pub mod compact;
#[cfg(not(feature = "max"))]
mod compact;
#[cfg(feature = "builtin")]
pub mod compile;
#[cfg(not(feature = "builtin"))]
mod compile;
#[cfg(feature = "max")]
pub mod exec;
#[cfg(not(feature = "max"))]
mod exec;
#[cfg(feature = "builtin")]
pub mod expr;
#[cfg(not(feature = "builtin"))]
mod expr;
#[cfg(feature = "max")]
pub mod page;
#[cfg(not(feature = "max"))]
mod page;
#[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 sortedfile;
#[cfg(not(feature = "max"))]
mod sortedfile;
#[cfg(feature = "max")]
pub mod sys;
#[cfg(not(feature = "max"))]
mod sys;
#[cfg(feature = "max")]
pub mod table;
#[cfg(not(feature = "max"))]
mod table;
#[cfg(feature = "builtin")]
pub mod value;
#[cfg(not(feature = "builtin"))]
mod value;
pub type Data = Arc<Vec<u8>>;
pub type DB = Rc<Database>;
pub type BuiltinMap = HashMap<String, (DataKind, CompileFunc)>;
pub struct Database {
pub file: AccessPagedData,
pub sys_schema: TablePtr,
pub sys_table: TablePtr,
pub sys_column: TablePtr,
pub sys_index: TablePtr,
pub sys_index_col: TablePtr,
pub sys_function: TablePtr,
pub bs: ByteStorage,
pub schemas: RefCell<HashMap<String, i64>>,
pub tables: RefCell<HashMap<ObjRef, TablePtr>>,
pub functions: RefCell<HashMap<ObjRef, FunctionPtr>>,
pub builtins: Arc<BuiltinMap>,
pub function_reset: Cell<bool>,
pub lastid: Cell<i64>,
pub err: Cell<bool>,
}
impl Database {
pub fn new(file: AccessPagedData, initsql: &str, builtins: Arc<BuiltinMap>) -> DB {
let mut dq = DummyTransaction {};
let is_new = file.is_new();
let mut tb = TableBuilder::new();
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", STRING), ("Def", BIGSTR)],
);
sys_schema.add_index(7, vec![0]);
sys_table.add_index(8, vec![1, 2]);
sys_column.add_index(9, vec![0]);
sys_index.add_index(10, vec![1]);
sys_index_col.add_index(11, vec![0]);
sys_function.add_index(12, vec![0, 1]);
let db = Rc::new(Database {
file,
sys_schema,
sys_table,
sys_column,
sys_index,
sys_index_col,
sys_function,
bs: ByteStorage::new(0),
schemas: newmap(),
functions: newmap(),
tables: newmap(),
builtins,
function_reset: Cell::new(false),
lastid: Cell::new(0),
err: Cell::new(false),
});
if is_new {
db.alloc_page(); }
db.bs.init(&db);
for t in &tb.list {
if !is_new {
t.id_gen.set(sys::get_id_gen(&db, t.id as u64));
}
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, 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)
GO
";
db.run(sysinit, &mut dq);
db.run(initsql, &mut dq);
db.save();
}
db
}
pub fn run(self: &DB, 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
);
println!("Run error {}", &err);
tr.set_error(err);
self.err.set(true);
}
}
pub fn run_timed(self: &DB, source: &str, tr: &mut dyn Transaction) {
let start = std::time::Instant::now();
self.run(source, tr);
println!(
"run_timed path={} run time={} micro sec.",
tr.arg(0, ""),
start.elapsed().as_micros()
);
}
pub(crate) fn go(self: &DB, 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 save(self: &DB) -> usize {
let op = if self.err.get() {
self.err.set(false);
SaveOp::RollBack
} else {
SaveOp::Save
};
self.bs.save(self, op);
let tm = &*self.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());
} else {
t.id_gen.set(sys::get_id_gen(self, t.id as u64));
}
t.id_gen_dirty.set(false);
}
}
for t in tm.values() {
t.save(self, op);
}
if self.function_reset.get() {
for function in self.functions.borrow().values() {
function.ilist.borrow_mut().clear();
}
self.functions.borrow_mut().clear();
self.function_reset.set(false);
}
self.file.save(op)
}
pub fn get_table(self: &DB, name: &ObjRef) -> Option<TablePtr> {
if let Some(t) = self.tables.borrow().get(name) {
return Some(t.clone());
}
sys::get_table(self, name)
}
pub(crate) fn get_function(self: &DB, name: &ObjRef) -> Option<FunctionPtr> {
if let Some(f) = self.functions.borrow().get(name) {
return Some(f.clone());
}
sys::get_function(self, name)
}
pub(crate) fn publish_table(&self, table: TablePtr) {
let name = table.info.name.clone();
self.tables.borrow_mut().insert(name, table);
}
pub(crate) fn encode(self: &DB, val: &Value, size: usize) -> u64 {
let bytes = match val {
Value::RcBinary(x) => &**x,
Value::ArcBinary(x) => &**x,
Value::String(x) => x.as_bytes(),
_ => {
return u64::MAX;
}
};
if bytes.len() < size {
return u64::MAX;
}
self.bs.encode(self, &bytes[size - 9..])
}
pub(crate) fn decode(self: &DB, code: u64, inline: usize) -> Vec<u8> {
self.bs.decode(self, code, inline)
}
pub(crate) fn delcode(self: &DB, code: u64) {
self.bs.delcode(self, code);
}
pub(crate) fn alloc_page(self: &DB) -> u64 {
self.file.alloc_page()
}
pub(crate) fn free_page(self: &DB, lpnum: u64) {
self.file.free_page(lpnum);
}
}
impl Drop for Database {
fn drop(&mut self) {
for function in self.functions.borrow().values() {
function.ilist.borrow_mut().clear();
}
}
}
struct TableBuilder {
alloc: i64,
list: Vec<TablePtr>,
}
impl TableBuilder {
fn new() -> Self {
Self {
alloc: 1,
list: Vec::new(),
}
}
fn nt(&mut self, name: &str, ct: &[(&str, DataType)]) -> TablePtr {
let id = self.alloc;
let root_page = id as u64;
self.alloc += 1;
let name = ObjRef::new("sys", name);
let info = ColInfo::new(name, ct);
let table = Table::new(id, root_page, 1, Rc::new(info));
self.list.push(table.clone());
table
}
}
pub trait Transaction: std::any::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) -> Rc<String> {
Rc::new(String::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: String);
fn get_error(&mut self) -> String {
String::new()
}
}
struct DummyTransaction {}
impl Transaction for DummyTransaction {
fn selected(&mut self, _values: &[Value]) {}
fn set_error(&mut self, err: String) {
println!("Error: {}", err);
}
}