///|
pub struct Database {
priv catalog : @plan.SchemaCatalog
}
///|
pub struct Connection {
priv database : Database
}
///|
pub struct ResultSet {
schema : Schema
priv rows : Array[Row]
priv mut next_row : Int
} derive(Eq, Debug)
///|
priv enum DbErrorKind {
InvalidArgument
Bind
Scan
Io
Execute
Unsupported
Internal
}
///|
pub fn Database::new() -> Database {
Database::{ catalog: @plan.new_schema_catalog() }
}
///|
pub fn Database::connect(self : Database) -> Connection {
Connection::{ database: self }
}
///|
pub async fn Connection::query(
self : Connection,
sql : String,
) -> Result[ResultSet, DbError] {
let parsed = @sql.parse_sql(sql)
match parsed {
Ok(query) =>
try {
let plan = @plan.plan_query_with_catalog(query, self.database.catalog) catch {
err => return Err(map_plan_error(err))
}
Ok(make_result_set(@execute.execute_plan(plan)))
} catch {
err => Err(map_execute_error(err))
}
Err(err) => Err(DbError::SqlSyntax(err.to_string()))
}
}
///|
pub fn ResultSet::schema(self : ResultSet) -> Schema {
self.schema
}
///|
pub fn ResultSet::next(self : ResultSet) -> Row? {
guard self.next_row < self.rows.length() else { return None }
let row = self.rows[self.next_row]
self.next_row += 1
Some(row)
}
///|
fn make_result_set(output : @execute.QueryOutput) -> ResultSet {
ResultSet::{
schema: output.schema,
rows: @engine.chunks_to_rows(output.chunks),
next_row: 0,
}
}
///|
fn map_execute_error(err : @execute.ExecuteError) -> DbError {
let (kind, message) = match err {
@execute.ExecuteError::InvalidArgument(message) =>
(DbErrorKind::InvalidArgument, message)
@execute.ExecuteError::Bind(message) => (DbErrorKind::Bind, message)
@execute.ExecuteError::Scan(message) => (DbErrorKind::Scan, message)
@execute.ExecuteError::Io(message) => (DbErrorKind::Io, message)
@execute.ExecuteError::Execute(message) => (DbErrorKind::Execute, message)
@execute.ExecuteError::Unsupported(message) =>
(DbErrorKind::Unsupported, message)
@execute.ExecuteError::Internal(message) => (DbErrorKind::Internal, message)
}
map_db_error(kind, message)
}
///|
fn map_plan_error(err : @plan.PlanError) -> DbError {
let (kind, message) = match err {
@plan.PlanError::InvalidArgument(message) =>
(DbErrorKind::InvalidArgument, message)
@plan.PlanError::Bind(message) => (DbErrorKind::Bind, message)
@plan.PlanError::Scan(message) => (DbErrorKind::Scan, message)
@plan.PlanError::Io(message) => (DbErrorKind::Io, message)
@plan.PlanError::Unsupported(message) => (DbErrorKind::Unsupported, message)
}
map_db_error(kind, message)
}
///|
fn map_db_error(kind : DbErrorKind, message : String) -> DbError {
match kind {
DbErrorKind::InvalidArgument => DbError::InvalidArgument(message)
DbErrorKind::Bind => DbError::Bind(message)
DbErrorKind::Scan => DbError::Scan(message)
DbErrorKind::Io => DbError::Io(message)
DbErrorKind::Execute => DbError::Execute(message)
DbErrorKind::Unsupported => DbError::Unsupported(message)
DbErrorKind::Internal => DbError::Internal(message)
}
}