use std::cmp::Ordering;
use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
fn fold(s: &str) -> String {
s.to_ascii_lowercase()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TableRef {
pub database: Option<String>,
pub schema: Option<String>,
pub name: String,
}
impl TableRef {
pub fn new(name: impl Into<String>) -> Self {
Self {
database: None,
schema: None,
name: name.into(),
}
}
pub fn qualified(schema: impl Into<String>, name: impl Into<String>) -> Self {
Self {
database: None,
schema: Some(schema.into()),
name: name.into(),
}
}
pub fn builder(name: impl Into<String>) -> TableRefBuilder {
TableRefBuilder {
database: None,
schema: None,
name: name.into(),
}
}
fn key(&self) -> (Option<String>, Option<String>, String) {
(
self.database.as_deref().map(fold),
self.schema.as_deref().map(fold),
fold(&self.name),
)
}
}
#[derive(Debug, Clone)]
pub struct TableRefBuilder {
database: Option<String>,
schema: Option<String>,
name: String,
}
impl TableRefBuilder {
#[must_use]
pub fn with_schema(mut self, schema: impl Into<String>) -> Self {
self.schema = Some(schema.into());
self
}
#[must_use]
pub fn with_database(mut self, database: impl Into<String>) -> Self {
self.database = Some(database.into());
self
}
#[must_use]
pub fn build(self) -> TableRef {
TableRef {
database: self.database,
schema: self.schema,
name: self.name,
}
}
}
impl PartialEq for TableRef {
fn eq(&self, other: &Self) -> bool {
self.key() == other.key()
}
}
impl Eq for TableRef {}
impl PartialOrd for TableRef {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TableRef {
fn cmp(&self, other: &Self) -> Ordering {
self.key().cmp(&other.key())
}
}
impl std::hash::Hash for TableRef {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.key().hash(state);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceRef {
pub table: TableRef,
pub column: String,
}
impl SourceRef {
pub fn new(table: TableRef, column: impl Into<String>) -> Self {
Self {
table,
column: column.into(),
}
}
fn key(&self) -> (Option<String>, Option<String>, String, String) {
let (db, schema, table) = self.table.key();
(db, schema, table, fold(&self.column))
}
}
impl PartialEq for SourceRef {
fn eq(&self, other: &Self) -> bool {
self.key() == other.key()
}
}
impl Eq for SourceRef {}
impl PartialOrd for SourceRef {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for SourceRef {
fn cmp(&self, other: &Self) -> Ordering {
self.key().cmp(&other.key())
}
}
impl std::hash::Hash for SourceRef {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.key().hash(state);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OutputColumn {
pub name: Option<String>,
pub position: usize,
pub sources: BTreeSet<SourceRef>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Lineage {
pub columns: Vec<OutputColumn>,
pub tables: BTreeSet<TableRef>,
pub target: Option<TableRef>,
}
impl Lineage {
#[must_use]
pub fn empty() -> Self {
Self {
columns: Vec::new(),
tables: BTreeSet::new(),
target: None,
}
}
#[must_use]
pub(crate) fn from_columns(columns: Vec<OutputColumn>) -> Self {
Self {
tables: tables_of(&columns),
columns,
target: None,
}
}
#[must_use]
pub(crate) fn from_write(target: TableRef, columns: Vec<OutputColumn>, input_tables: BTreeSet<TableRef>) -> Self {
let mut tables = input_tables;
tables.extend(tables_of(&columns));
Self {
columns,
tables,
target: Some(target),
}
}
}
fn tables_of(columns: &[OutputColumn]) -> BTreeSet<TableRef> {
columns
.iter()
.flat_map(|c| c.sources.iter().map(|s| s.table.clone()))
.collect()
}