mod sql;
use itertools::{Either, Itertools};
pub(crate) use sql::{find_referenced_tables_in_schema, parse_select_query};
use crate::ducklake::DucklakeConnection;
use crate::{DucklakeResult, Table, TableName};
#[derive(Clone)]
pub struct View {
conn: DucklakeConnection,
id: i64,
}
#[derive(Clone)]
pub(crate) struct ViewInfo {
pub name: TableName,
pub sql: String,
pub dialect: String,
pub column_aliases: Option<Vec<String>>,
pub tags: Vec<crate::Tag>,
}
#[derive(Clone)]
pub struct ViewDefinition {
pub sql: String,
pub dialect: String,
pub default_schema: String,
pub tables: Vec<(TableName, Table)>,
pub views: Vec<(TableName, View)>,
}
impl View {
pub(crate) fn new(conn: DucklakeConnection, id: i64) -> Self {
Self { conn, id }
}
pub async fn name(&self) -> DucklakeResult<TableName> {
let snapshot = self.conn.current_snapshot();
let catalog = snapshot.catalog().await?;
let view = catalog.view(self.id)?;
Ok(view.name().clone())
}
pub async fn sql(&self) -> DucklakeResult<String> {
let snapshot = self.conn.current_snapshot();
let catalog = snapshot.catalog().await?;
Ok(catalog.view(self.id)?.sql().to_string())
}
pub async fn column_aliases(&self) -> DucklakeResult<Option<Vec<String>>> {
let snapshot = self.conn.current_snapshot();
let catalog = snapshot.catalog().await?;
Ok(catalog.view(self.id)?.column_aliases())
}
pub async fn tags(&self) -> DucklakeResult<Vec<crate::Tag>> {
let snapshot = self.conn.current_snapshot();
let catalog = snapshot.catalog().await?;
Ok(catalog.view(self.id)?.tags())
}
pub async fn definition(&self) -> DucklakeResult<ViewDefinition> {
let snapshot = self.conn.current_snapshot();
let catalog = snapshot.catalog().await?;
let view = catalog.view(self.id)?;
let sql = view.sql().to_string();
let dialect = view.dialect().to_string();
let default_schema = view.name().schema.clone();
let (tables, views) = find_referenced_tables_in_schema(&sql, &dialect, &default_schema)?
.into_iter()
.partition_map(|name| match catalog.table(&name) {
Ok(table) => {
let schema_id = table
.parent_schema()
.id()
.expect("referenced table schema must have an ID");
let table_id = table.id().expect("referenced table must have an ID");
Either::Left((name, Table::new(self.conn.clone(), schema_id, table_id)))
}
Err(_) => {
let view_id = catalog
.view(&name)
.expect("referenced relation must be a catalog table or view")
.id()
.expect("referenced view must have an ID");
Either::Right((name, View::new(self.conn.clone(), view_id)))
}
});
Ok(ViewDefinition {
sql,
dialect,
default_schema,
tables,
views,
})
}
pub async fn delete(&self) -> DucklakeResult<()> {
let mut tx = self.conn.transaction(None).await?;
let name = tx.catalog().view(self.id)?.name().clone();
tx.delete_view(&name)?;
tx.commit().await
}
}