dbmcp_sql_lineage/
model.rs1use std::cmp::Ordering;
7use std::collections::BTreeSet;
8
9use serde::{Deserialize, Serialize};
10
11fn fold(s: &str) -> String {
13 s.to_ascii_lowercase()
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct TableRef {
21 pub database: Option<String>,
23 pub schema: Option<String>,
25 pub name: String,
27}
28
29impl TableRef {
30 pub fn new(name: impl Into<String>) -> Self {
32 Self {
33 database: None,
34 schema: None,
35 name: name.into(),
36 }
37 }
38
39 pub fn qualified(schema: impl Into<String>, name: impl Into<String>) -> Self {
41 Self {
42 database: None,
43 schema: Some(schema.into()),
44 name: name.into(),
45 }
46 }
47
48 pub fn builder(name: impl Into<String>) -> TableRefBuilder {
50 TableRefBuilder {
51 database: None,
52 schema: None,
53 name: name.into(),
54 }
55 }
56
57 fn key(&self) -> (Option<String>, Option<String>, String) {
59 (
60 self.database.as_deref().map(fold),
61 self.schema.as_deref().map(fold),
62 fold(&self.name),
63 )
64 }
65}
66
67#[derive(Debug, Clone)]
69pub struct TableRefBuilder {
70 database: Option<String>,
71 schema: Option<String>,
72 name: String,
73}
74
75impl TableRefBuilder {
76 #[must_use]
78 pub fn with_schema(mut self, schema: impl Into<String>) -> Self {
79 self.schema = Some(schema.into());
80 self
81 }
82
83 #[must_use]
85 pub fn with_database(mut self, database: impl Into<String>) -> Self {
86 self.database = Some(database.into());
87 self
88 }
89
90 #[must_use]
92 pub fn build(self) -> TableRef {
93 TableRef {
94 database: self.database,
95 schema: self.schema,
96 name: self.name,
97 }
98 }
99}
100
101impl PartialEq for TableRef {
102 fn eq(&self, other: &Self) -> bool {
103 self.key() == other.key()
104 }
105}
106
107impl Eq for TableRef {}
108
109impl PartialOrd for TableRef {
110 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
111 Some(self.cmp(other))
112 }
113}
114
115impl Ord for TableRef {
116 fn cmp(&self, other: &Self) -> Ordering {
117 self.key().cmp(&other.key())
118 }
119}
120
121impl std::hash::Hash for TableRef {
122 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
123 self.key().hash(state);
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct SourceRef {
132 pub table: TableRef,
134 pub column: String,
136}
137
138impl SourceRef {
139 pub fn new(table: TableRef, column: impl Into<String>) -> Self {
141 Self {
142 table,
143 column: column.into(),
144 }
145 }
146
147 fn key(&self) -> (Option<String>, Option<String>, String, String) {
149 let (db, schema, table) = self.table.key();
150 (db, schema, table, fold(&self.column))
151 }
152}
153
154impl PartialEq for SourceRef {
155 fn eq(&self, other: &Self) -> bool {
156 self.key() == other.key()
157 }
158}
159
160impl Eq for SourceRef {}
161
162impl PartialOrd for SourceRef {
163 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
164 Some(self.cmp(other))
165 }
166}
167
168impl Ord for SourceRef {
169 fn cmp(&self, other: &Self) -> Ordering {
170 self.key().cmp(&other.key())
171 }
172}
173
174impl std::hash::Hash for SourceRef {
175 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
176 self.key().hash(state);
177 }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
182pub struct OutputColumn {
183 pub name: Option<String>,
185 pub position: usize,
187 pub sources: BTreeSet<SourceRef>,
189}
190
191#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
193pub struct Lineage {
194 pub columns: Vec<OutputColumn>,
196 pub tables: BTreeSet<TableRef>,
198 pub target: Option<TableRef>,
200}
201
202impl Lineage {
203 #[must_use]
205 pub fn empty() -> Self {
206 Self {
207 columns: Vec::new(),
208 tables: BTreeSet::new(),
209 target: None,
210 }
211 }
212
213 #[must_use]
215 pub(crate) fn from_columns(columns: Vec<OutputColumn>) -> Self {
216 Self {
217 tables: tables_of(&columns),
218 columns,
219 target: None,
220 }
221 }
222
223 #[must_use]
228 pub(crate) fn from_write(target: TableRef, columns: Vec<OutputColumn>, input_tables: BTreeSet<TableRef>) -> Self {
229 let mut tables = input_tables;
230 tables.extend(tables_of(&columns));
231 Self {
232 columns,
233 tables,
234 target: Some(target),
235 }
236 }
237}
238
239fn tables_of(columns: &[OutputColumn]) -> BTreeSet<TableRef> {
241 columns
242 .iter()
243 .flat_map(|c| c.sources.iter().map(|s| s.table.clone()))
244 .collect()
245}