1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use super::*;
impl BasiliqStoreBuilder {
/// Take a first stab at parsing the relationships
/// Every relationships will first be expressed as a OneToMany and ManyToOne relationships
pub(super) fn build_relationships_base(
tables: &BTreeMap<BasiliqStoreTableIdentifier, BasiliqStoreTableBuilder>,
relationships: BTreeMap<
BasiliqStoreTableIdentifier,
BTreeMap<String, (BasiliqStoreTableIdentifier, i16)>,
>,
) -> BTreeSet<BasiliqStoreRelationshipData> {
let mut res: BTreeSet<BasiliqStoreRelationshipData> = BTreeSet::new();
'tables: for (main_table_name, rels) in relationships.iter() {
// Iterate over every tables containing relationships
if !tables.contains_key(main_table_name) {
warn!("Unknown table `{}`, skipping...", main_table_name);
continue 'tables;
}
let ltable = match tables.get(main_table_name) {
// Get the type of the foreign table, fails if can't be found
Some(x) => x,
None => {
warn!("Unknown table `{}`, skipping...", main_table_name);
continue 'tables;
}
};
'rels: for (rel_key, (rel_type, rel_field_index)) in rels {
// Iterate over every relationships in that table
let ftable = match tables.get(rel_type) {
// Get the type of the foreign table, fails if can't be found
Some(x) => x,
None => {
warn!("Unknown table `{}`, skipping...", rel_type);
continue 'tables;
}
};
let lkey_col = match ltable // Get the column name of the field that is referenced, fails if can't be found
.table
.columns_by_name()
.get(rel_key)
{
Some(x) => x,
None => {
warn!(
"Unknown column {} for table `{}`, skipping...",
main_table_name, main_table_name
);
continue 'rels;
}
};
let fkey_col = match ftable // Get the column name of the field that is referenced, fails if can't be found
.table
.columns_by_id()
.get(rel_field_index)
{
Some(x) => x,
None => {
warn!(
"Unknown column {} for table `{}`, skipping...",
rel_field_index, rel_type
);
continue 'rels;
}
};
res.insert(
// Insert the ManyToOne relationship from the main type to the foreign type
BasiliqStoreRelationshipData {
ltable: main_table_name.clone(),
ftable: rel_type.clone(),
ffield_name: ArcStr::from(fkey_col.column().name()),
lfield_name: ArcStr::from(rel_key),
type_: BasiliqStoreRelationshipType::ManyToOne(false),
optional: !lkey_col.column().non_null(),
},
);
if ltable != ftable
// No need to insert both if it's a self reference
{
res.insert(
// Insert the OneToMany relationship from the foreign type to the main type
BasiliqStoreRelationshipData {
ltable: rel_type.clone(),
ftable: main_table_name.clone(),
ffield_name: ArcStr::from(rel_key),
lfield_name: ArcStr::from(fkey_col.column().name()),
type_: BasiliqStoreRelationshipType::OneToMany(false),
optional: !lkey_col.column().non_null(),
},
);
}
}
}
res
}
/// Pull together relationships that can be expressed as ManyToMany through another intermediate table
pub(super) fn build_relationships_many(
mut relationships: BTreeSet<BasiliqStoreRelationshipData>,
) -> BTreeSet<BasiliqStoreRelationshipData> {
// A store that'll be used for building. It'll map each table with every incoming relationships
let mut set: BTreeMap<BasiliqStoreTableIdentifier, Vec<BasiliqStoreRelationshipData>> =
BTreeMap::new();
// Fill the set
fill_relationships_set(&mut set, &relationships);
for (ident, elements) in set.into_iter() {
// For every table that has relationships
for element in elements.iter() {
for other_element in elements.iter() {
// Map together the relationships that a common node
if other_element == element {
// Skip if the same type
continue;
}
let mut old_rel_1 = element.clone();
let mut old_rel_2 = other_element.clone();
relationships.remove(&old_rel_1);
relationships.remove(&old_rel_2);
old_rel_1.type_ = BasiliqStoreRelationshipType::OneToMany(true);
old_rel_2.type_ = BasiliqStoreRelationshipType::OneToMany(true);
relationships.insert(old_rel_1);
relationships.insert(old_rel_2);
relationships.insert(BasiliqStoreRelationshipData {
ltable: element.ltable().clone(),
lfield_name: element.lfield_name().clone(),
ftable: other_element.ltable().clone(),
ffield_name: other_element.lfield_name().clone(),
type_: BasiliqStoreRelationshipType::ManyToMany(
BasiliqStoreRelationshipManyToManyData {
bucket: ident.clone(),
lfield_name: element.ffield_name().clone(),
ffield_name: other_element.ffield_name().clone(),
},
),
optional: true,
});
}
}
}
relationships
}
}
// It'll map each table with every incoming relationships
fn fill_relationships_set(
set: &mut BTreeMap<BasiliqStoreTableIdentifier, Vec<BasiliqStoreRelationshipData>>,
relationships: &BTreeSet<BasiliqStoreRelationshipData>,
) {
for rel_data in relationships.iter() {
if let BasiliqStoreRelationshipType::OneToMany(_) = rel_data.type_() {
if let Some(x) = set.get_mut(&rel_data.ftable()) {
x.push(rel_data.clone());
} else {
set.insert(rel_data.ftable().clone(), vec![rel_data.clone()]);
}
}
}
}