use rudb_bind::Bound;
use rudb_catalog::Catalog;
use rudb_common::{Error, Field, Result, Value};
use crate::result::QueryResult;
#[derive(Debug)]
pub struct Database {
catalog: Catalog,
}
impl Default for Database {
fn default() -> Self {
Self::new()
}
}
impl Database {
#[must_use]
pub fn new() -> Self {
Self { catalog: Catalog::new() }
}
#[must_use]
pub fn catalog(&self) -> &Catalog {
&self.catalog
}
pub fn catalog_mut(&mut self) -> &mut Catalog {
&mut self.catalog
}
pub fn create_table(&mut self, name: &str, columns: Vec<Field>) -> Result<()> {
let parts: Vec<&str> = name.split('.').collect();
let resolved = self.catalog.resolve_for_create(&parts)?;
self.catalog.create_table(resolved, columns)
}
pub fn drop_table(&mut self, name: &str) -> Result<()> {
let parts: Vec<&str> = name.split('.').collect();
let resolved = self.catalog.resolve(&parts)?;
self.catalog.drop_table(&resolved)
}
pub fn append(&mut self, name: &str, rows: &[Vec<Value>]) -> Result<()> {
let parts: Vec<&str> = name.split('.').collect();
let resolved = self.catalog.resolve(&parts)?;
self.catalog.table_mut(&resolved)?.append_rows(rows)
}
pub fn table_len(&self, name: &str) -> Result<usize> {
let parts: Vec<&str> = name.split('.').collect();
let resolved = self.catalog.resolve(&parts)?;
Ok(self.catalog.table(&resolved)?.rows().len())
}
pub fn query(&self, sql: &str) -> Result<QueryResult> {
let plan = rudb_bind::bind_sql(sql, &self.catalog)?;
self.run(&plan)
}
pub fn execute(&mut self, sql: &str) -> Result<QueryResult> {
match rudb_bind::bind_statement_sql(sql, &self.catalog)? {
Bound::Query(plan) => self.run(&plan),
Bound::CreateTable(create) => {
self.run_create_table(create)?;
Ok(QueryResult::empty())
}
Bound::DropTable(drop) => {
for name in &drop.names {
self.catalog.drop_table(name)?;
}
Ok(QueryResult::empty())
}
Bound::Insert(insert) => {
let result = self.run(&insert.source)?;
let table = self.catalog.table_mut(&insert.name)?;
for chunk in result.chunks() {
table.append(chunk.clone())?;
}
Ok(QueryResult::empty())
}
}
}
fn run_create_table(&mut self, create: rudb_bind::CreateTable) -> Result<()> {
if create.if_not_exists && self.catalog.table(&create.name).is_ok() {
return Ok(());
}
let rows = match &create.source {
Some(plan) => Some(self.run(plan)?),
None => None,
};
if create.or_replace && self.catalog.table(&create.name).is_ok() {
self.catalog.drop_table(&create.name)?;
}
self.catalog.create_table(create.name.clone(), create.columns)?;
if let Some(rows) = rows {
let table = self.catalog.table_mut(&create.name)?;
for chunk in rows.chunks() {
table.append(chunk.clone())?;
}
}
Ok(())
}
fn run(&self, plan: &rudb_plan::Plan) -> Result<QueryResult> {
let mut root = rudb_exec::build(plan, &self.catalog)?;
let names = root.schema().names();
let types = root.schema().types();
let mut chunks = Vec::new();
while let Some(chunk) = root.next()? {
if chunk.is_empty() {
continue;
}
chunks.push(chunk.flatten()?);
}
Ok(QueryResult::new(names, types, chunks))
}
pub fn plan(&self, sql: &str) -> Result<String> {
let plan = rudb_bind::bind_sql(sql, &self.catalog)?;
Ok(plan.to_string())
}
pub fn value(&self, sql: &str) -> Result<Value> {
let result = self.query(sql)?;
if result.len() != 1 || result.width() != 1 {
return Err(Error::invalid_input(format!(
"expected one row of one column, got {} rows of {} columns",
result.len(),
result.width()
)));
}
Ok(result.value_at(0, 0))
}
}