uqa_execution/batch/
outer_scope.rs1use std::collections::{HashMap, HashSet};
10
11use uqa_sql::ast::ColumnType;
12
13use super::{ColumnIdentity, RowSchema, SchemaBuildMetadata, NULL_SLOT};
14
15impl RowSchema {
16 pub fn with_typed_outer_scope(
18 input: &Self,
19 outer_columns: &[(String, Option<ColumnType>)],
20 ) -> Self {
21 let identities = outer_columns
22 .iter()
23 .map(|(column, ty)| (ColumnIdentity::unqualified(column), ty.clone()))
24 .collect::<Vec<_>>();
25 Self::with_typed_outer_identities(input, &identities)
26 }
27
28 pub fn with_typed_outer_identities(
30 input: &Self,
31 outer_columns: &[(ColumnIdentity, Option<ColumnType>)],
32 ) -> Self {
33 let outer_base = input.physical_width();
34 let current_qualifiers = input
35 .index
36 .qualified
37 .keys()
38 .chain(input.index.aliases.keys())
39 .filter_map(ColumnIdentity::qualifier)
40 .collect::<HashSet<_>>();
41 let mut current_unqualified = input
42 .identities()
43 .iter()
44 .map(ColumnIdentity::column)
45 .collect::<HashSet<_>>();
46 current_unqualified.extend(
47 input
48 .index
49 .aliases
50 .keys()
51 .filter(|identity| identity.qualifier().is_none())
52 .map(ColumnIdentity::column),
53 );
54
55 let mut aliases = input.index.aliases.clone();
56 let mut alias_types = input.index.cold.aliases.clone();
57 let mut ambiguous_qualified = input.index.ambiguous_qualified.clone();
58 let mut outer_exact = HashSet::<&str>::new();
59 let mut outer_qualified = HashMap::<&str, Vec<usize>>::new();
60 let mut outer_qualified_counts = HashMap::<ColumnIdentity, usize>::new();
61 for (identity, _) in outer_columns {
62 if identity
63 .qualifier()
64 .is_some_and(|qualifier| !current_qualifiers.contains(qualifier))
65 {
66 *outer_qualified_counts.entry(identity.clone()).or_default() += 1;
67 }
68 }
69 for (position, (identity, ty)) in outer_columns.iter().enumerate() {
70 let slot = outer_base + position;
71 if let Some(qualifier) = identity.qualifier() {
72 if !current_qualifiers.contains(qualifier) {
73 if outer_qualified_counts.get(identity) == Some(&1) {
74 aliases.insert(identity.clone(), slot);
75 alias_types.insert(identity.clone(), ty.clone());
76 } else {
77 aliases.remove(identity);
78 alias_types.remove(identity);
79 ambiguous_qualified.insert(identity.clone());
80 }
81 }
82 outer_qualified
83 .entry(identity.column())
84 .or_default()
85 .push(position);
86 } else {
87 outer_exact.insert(identity.column());
88 if !current_unqualified.contains(identity.column()) {
89 aliases.insert(identity.clone(), slot);
90 alias_types.insert(identity.clone(), ty.clone());
91 }
92 }
93 }
94
95 let mut outer_ambiguous = HashSet::new();
96 for (column, positions) in outer_qualified {
97 if current_unqualified.contains(column) || outer_exact.contains(column) {
98 continue;
99 }
100 match positions.as_slice() {
101 [position] => {
102 let identity = ColumnIdentity::unqualified(column);
103 aliases.insert(identity.clone(), outer_base + position);
104 alias_types.insert(identity, outer_columns[*position].1.clone());
105 }
106 [_, _, ..] => {
107 outer_ambiguous.insert(Box::<str>::from(column));
108 }
109 [] => {}
110 }
111 }
112
113 Self::from_typed_parts_with_aliases_and_exact_precedence(
114 input.columns().to_vec(),
115 input.identities().to_vec(),
116 input.column_types().to_vec(),
117 input.index.slots.to_vec(),
118 input.physical_width() + outer_columns.len(),
119 SchemaBuildMetadata {
120 aliases,
121 alias_types,
122 extra_ambiguous_unqualified: outer_ambiguous,
123 extra_ambiguous_qualified: ambiguous_qualified,
124 ..SchemaBuildMetadata::default()
125 },
126 )
127 }
128
129 pub(crate) fn with_outer_schema(input: &Self, outer: &Self) -> Self {
131 let outer_base = input.physical_width();
132 let current_qualifiers = input
133 .index
134 .qualified
135 .keys()
136 .chain(input.index.aliases.keys())
137 .filter_map(ColumnIdentity::qualifier)
138 .collect::<HashSet<_>>();
139 let mut current_unqualified = input
140 .index
141 .unqualified
142 .keys()
143 .map(std::convert::AsRef::as_ref)
144 .collect::<HashSet<_>>();
145 current_unqualified.extend(
146 input
147 .index
148 .aliases
149 .keys()
150 .filter(|identity| identity.qualifier().is_none())
151 .map(ColumnIdentity::column),
152 );
153
154 let shifted_slot = |slot: usize| {
155 if slot == NULL_SLOT {
156 NULL_SLOT
157 } else {
158 outer_base + slot
159 }
160 };
161 let mut aliases = input.index.aliases.clone();
162 let mut alias_types = input.index.cold.aliases.clone();
163 let mut ambiguous_unqualified = input.index.ambiguous_unqualified.clone();
164 let mut ambiguous_qualified = input.index.ambiguous_qualified.clone();
165
166 let qualified_identities = outer
167 .index
168 .qualified
169 .keys()
170 .chain(
171 outer
172 .index
173 .aliases
174 .keys()
175 .filter(|identity| identity.qualifier().is_some()),
176 )
177 .chain(outer.index.ambiguous_qualified.iter())
178 .cloned()
179 .collect::<HashSet<_>>();
180 for identity in qualified_identities {
181 let qualifier = identity
182 .qualifier()
183 .expect("qualified outer identity has a qualifier");
184 if current_qualifiers.contains(qualifier) {
185 continue;
186 }
187 if outer.qualified_column_is_ambiguous(qualifier, identity.column()) {
188 aliases.remove(&identity);
189 alias_types.remove(&identity);
190 ambiguous_qualified.insert(identity);
191 continue;
192 }
193 let slot = outer
194 .qualified_slot(qualifier, identity.column())
195 .map_or(NULL_SLOT, shifted_slot);
196 let ty = outer.qualified_type(qualifier, identity.column()).cloned();
197 aliases.insert(identity.clone(), slot);
198 alias_types.insert(identity, ty);
199 }
200
201 let outer_unqualified = outer
202 .index
203 .unqualified
204 .keys()
205 .map(std::convert::AsRef::as_ref)
206 .chain(
207 outer
208 .index
209 .aliases
210 .keys()
211 .filter(|identity| identity.qualifier().is_none())
212 .map(ColumnIdentity::column),
213 )
214 .chain(
215 outer
216 .index
217 .ambiguous_unqualified
218 .iter()
219 .map(std::convert::AsRef::as_ref),
220 )
221 .collect::<HashSet<_>>();
222 for column in outer_unqualified {
223 if current_unqualified.contains(column) {
224 continue;
225 }
226 let identity = ColumnIdentity::unqualified(column);
227 if outer.column_is_ambiguous(column) {
228 aliases.remove(&identity);
229 alias_types.remove(&identity);
230 ambiguous_unqualified.insert(Box::<str>::from(column));
231 continue;
232 }
233 let slot = outer.column_slot(column).map_or(NULL_SLOT, shifted_slot);
234 aliases.insert(identity.clone(), slot);
235 alias_types.insert(identity, outer.type_of(column).cloned());
236 }
237
238 Self::from_typed_parts_with_aliases_and_exact_precedence(
239 input.columns().to_vec(),
240 input.identities().to_vec(),
241 input.column_types().to_vec(),
242 input.index.slots.to_vec(),
243 input.physical_width() + outer.physical_width(),
244 SchemaBuildMetadata {
245 aliases,
246 alias_types,
247 extra_ambiguous_unqualified: ambiguous_unqualified,
248 extra_ambiguous_qualified: ambiguous_qualified,
249 ..SchemaBuildMetadata::default()
250 },
251 )
252 }
253}