use crate::{
bytes::ByteStorage,
compact::CompactFile,
exec::EvalEnv,
expr::*,
page::{Page, PagePtr},
parse::Parser,
pstore::AccessPagedData,
run::*,
sortedfile::{Asc, Id, Record, SortedFile},
stg::Storage,
table::{ColInfo, IndexInfo, Row, SaveOp, Table, TablePtr},
util::newmap,
value::{get_bytes, Value},
};
use std::{
cell::{Cell, RefCell},
cmp::Ordering,
collections::HashMap,
panic,
rc::Rc,
sync::{Arc, Mutex},
};
#[macro_use]
pub mod util;
pub mod builtin;
pub mod bytes;
pub mod cache;
pub mod cexp;
pub mod compact;
pub mod compile;
pub mod exec;
pub mod expr;
pub mod genquery;
pub mod init;
pub mod page;
pub mod parse;
pub mod pstore;
pub mod run;
pub mod sortedfile;
pub mod stg;
pub mod sys;
pub mod table;
pub mod value;
pub mod web;
pub type Data = Arc<Vec<u8>>;
pub type DB = Rc<Database>;
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 bs: ByteStorage,
pub schemas: RefCell<HashMap<String, i64>>,
pub tables: RefCell<HashMap<ObjRef, TablePtr>>,
pub functions: RefCell<HashMap<ObjRef, FunctionPtr>>,
pub builtins: RefCell<HashMap<String, (DataKind, CompileFunc)>>,
pub function_reset: Cell<bool>,
pub lastid: Cell<i64>,
pub err: Cell<bool>,
}
impl Database {
pub fn new(file: AccessPagedData, initsql: &str) -> DB {
let mut dq = DummyQuery {};
let is_new = file.is_new();
let mut tb = TableBuilder::new();
let sys_schema = tb.nt("sys", "Schema", &[("Name", STRING)]);
let sys_table = tb.nt(
"sys",
"Table",
&[
("Root", BIGINT),
("Schema", BIGINT),
("Name", STRING),
("IsView", TINYINT),
("Def", STRING),
("IdGen", BIGINT),
],
);
let sys_column = tb.nt(
"sys",
"Column",
&[("Table", BIGINT), ("Name", STRING), ("Type", BIGINT)],
);
let sys_index = tb.nt(
"sys",
"Index",
&[("Root", BIGINT), ("Table", BIGINT), ("Name", STRING)],
);
let sys_index_col = tb.nt(
"sys",
"IndexColumn",
&[("Index", BIGINT), ("ColId", BIGINT)],
);
sys_table.add_index(6, vec![1, 2]);
sys_column.add_index(7, vec![0]);
sys_index.add_index(8, vec![1]);
sys_index_col.add_index(9, vec![0]);
let db = Rc::new(Database {
file,
sys_schema,
sys_table,
sys_column,
sys_index,
sys_index_col,
bs: ByteStorage::new(0),
schemas: newmap(),
functions: newmap(),
tables: newmap(),
builtins: newmap(),
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 bigint, Schema bigint, Name string, IsView tinyint, Def string, IdGen bigint )
CREATE TABLE sys.Column( Table bigint, Name string, Type bigint )
CREATE TABLE sys.Index( Root bigint, Table bigint, Name string )
CREATE TABLE sys.IndexColumn( Index bigint, ColId bigint )
GO
CREATE INDEX BySchemaName ON sys.Table(Schema,Name)
GO
CREATE INDEX ByTable ON sys.Column(Table)
CREATE INDEX ByTable ON sys.Index(Table)
CREATE INDEX ByIndex ON sys.IndexColumn(Index)
GO
CREATE TABLE sys.Function( Schema bigint, Name string, Def string )
GO
CREATE INDEX BySchemaName ON sys.Function(Schema,Name)
GO
";
db.run(sysinit, &mut dq);
db.run(initsql, &mut dq);
db.save();
}
builtin::register_builtins(&db);
db
}
pub fn register(self: &DB, name: &str, typ: DataKind, cf: CompileFunc) {
self.builtins
.borrow_mut()
.insert(name.to_string(), (typ, cf));
}
pub fn run(self: &DB, source: &str, qy: &mut dyn Query) {
if let Some(e) = self.go(source, qy) {
let err = format!(
"{} in {} at line {} column {}.",
e.msg, e.rname, e.line, e.column
);
println!("Run error {}", &err);
qy.set_error(err);
self.err.set(true);
}
}
pub fn run_timed(self: &DB, source: &str, qy: &mut dyn Query) {
let start = std::time::Instant::now();
self.run(source, qy);
println!(
"run_timed path={} run time={} micro sec.",
qy.arg(0, ""),
start.elapsed().as_micros()
);
}
pub fn go(self: &DB, source: &str, qy: &mut dyn Query) -> Option<SqlError> {
let mut p = Parser::new(source, self);
let result = std::panic::catch_unwind(panic::AssertUnwindSafe(|| {
p.batch(qy);
}));
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) {
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 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 fn publish_table(&self, table: TablePtr) {
let name = table.info.name.clone();
self.tables.borrow_mut().insert(name, table);
}
pub fn encode(self: &DB, val: &Value) -> u64 {
let bytes = match val {
Value::Binary(x) => x,
Value::String(x) => x.as_bytes(),
_ => {
return u64::MAX;
}
};
if bytes.len() < 16 {
return u64::MAX;
}
self.bs.encode(self, &bytes[7..])
}
pub fn decode(self: &DB, code: u64) -> Vec<u8> {
self.bs.decode(self, code)
}
pub fn delcode(self: &DB, code: u64) {
self.bs.delcode(self, code);
}
pub fn alloc_page(self: &DB) -> u64 {
self.file.alloc_page()
}
pub 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, schema: &str, name: &str, ct: &[(&str, DataType)]) -> TablePtr {
let id = self.alloc;
let root_page = id as u64;
self.alloc += 1;
let name = ObjRef::new(schema, 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 Query {
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) -> Rc<Vec<u8>> {
Rc::new(Vec::new())
}
fn set_error(&mut self, err: String);
fn get_error(&mut self) -> String {
String::new()
}
}
struct DummyQuery {}
impl Query for DummyQuery {
fn selected(&mut self, _values: &[Value]) {}
fn set_error(&mut self, err: String) {
println!("Error: {}", err);
}
}