18
18
//! DFSchema is an extended schema struct that DataFusion uses to provide support for
19
19
//! fields with optional relation names.
20
20
21
- use std:: collections:: { HashMap , HashSet } ;
21
+ use std:: collections:: { BTreeMap , HashMap , HashSet } ;
22
22
use std:: convert:: TryFrom ;
23
23
use std:: fmt:: { Display , Formatter } ;
24
24
use std:: hash:: Hash ;
@@ -42,6 +42,8 @@ pub type DFSchemaRef = Arc<DFSchema>;
42
42
pub struct DFSchema {
43
43
/// Fields
44
44
fields : Vec < DFField > ,
45
+ // For searching column id by field name
46
+ fields_index : BTreeMap < String , Vec < usize > > ,
45
47
/// Additional metadata in form of key value pairs
46
48
metadata : HashMap < String , String > ,
47
49
/// Stores functional dependencies in the schema.
@@ -53,6 +55,7 @@ impl DFSchema {
53
55
pub fn empty ( ) -> Self {
54
56
Self {
55
57
fields : vec ! [ ] ,
58
+ fields_index : BTreeMap :: new ( ) ,
56
59
metadata : HashMap :: new ( ) ,
57
60
functional_dependencies : FunctionalDependencies :: empty ( ) ,
58
61
}
@@ -104,9 +107,24 @@ impl DFSchema {
104
107
) ) ;
105
108
}
106
109
}
110
+
111
+ let mut fields_index: BTreeMap < String , Vec < usize > > = BTreeMap :: new ( ) ;
112
+ for ( idx, field) in fields. iter ( ) . enumerate ( ) {
113
+ let key = match field. qualifier {
114
+ Some ( _) => field. name ( ) . to_owned ( ) ,
115
+ None => Column :: from_qualified_name ( field. name ( ) ) . name ,
116
+ } ;
117
+ field. qualified_name ( ) ;
118
+ fields_index
119
+ . entry ( key)
120
+ . and_modify ( |vec| vec. push ( idx) )
121
+ . or_insert ( vec ! [ idx] ) ;
122
+ }
123
+
107
124
Ok ( Self {
108
125
fields,
109
126
metadata,
127
+ fields_index,
110
128
functional_dependencies : FunctionalDependencies :: empty ( ) ,
111
129
} )
112
130
}
@@ -161,6 +179,16 @@ impl DFSchema {
161
179
} ;
162
180
if !duplicated_field {
163
181
self . fields . push ( field. clone ( ) ) ;
182
+
183
+ let idx = self . fields . len ( ) - 1 ;
184
+ let key = match field. qualifier {
185
+ Some ( _) => field. name ( ) . to_owned ( ) ,
186
+ None => Column :: from_qualified_name ( field. name ( ) ) . name ,
187
+ } ;
188
+ self . fields_index
189
+ . entry ( key)
190
+ . and_modify ( |vec| vec. push ( idx) )
191
+ . or_insert ( vec ! [ idx] ) ;
164
192
}
165
193
}
166
194
self . metadata . extend ( other_schema. metadata . clone ( ) )
@@ -208,32 +236,50 @@ impl DFSchema {
208
236
qualifier : Option < & TableReference > ,
209
237
name : & str ,
210
238
) -> Result < Option < usize > > {
211
- let mut matches = self
212
- . fields
239
+ let key = match qualifier {
240
+ Some ( _) => name. to_owned ( ) ,
241
+ None => Column :: from_qualified_name ( name) . name ,
242
+ } ;
243
+ let Some ( matched_fields_idx) = self . fields_index . get ( & key) else {
244
+ return Ok ( None ) ;
245
+ } ;
246
+
247
+ let Some ( qualifier) = qualifier else {
248
+ // field to lookup is unqualified, no need to compare qualifier
249
+ let mut matches = matched_fields_idx
250
+ . iter ( )
251
+ . map ( |idx| ( * idx, self . field ( * idx) ) )
252
+ . filter ( |( _, field) | field. name ( ) == name)
253
+ . map ( |( idx, _) | idx) ;
254
+ return Ok ( matches. next ( ) ) ;
255
+ } ;
256
+
257
+ // field to lookup is qualified.
258
+ let mut matches = matched_fields_idx
213
259
. iter ( )
214
- . enumerate ( )
215
- . filter ( |( _, field) | match ( qualifier , & field. qualifier ) {
260
+ . map ( |idx| ( * idx , self . field ( * idx ) ) )
261
+ . filter ( |( _, field) | match & field. qualifier {
216
262
// field to lookup is qualified.
217
263
// current field is qualified and not shared between relations, compare both
218
264
// qualifier and name.
219
- ( Some ( q ) , Some ( field_q ) ) => {
220
- q . resolved_eq ( field_q ) && field. name ( ) == name
265
+ Some ( field_qualifier ) => {
266
+ qualifier . resolved_eq ( field_qualifier ) && field. name ( ) == name
221
267
}
222
268
// field to lookup is qualified but current field is unqualified.
223
- ( Some ( qq ) , None ) => {
269
+ None => {
224
270
// the original field may now be aliased with a name that matches the
225
271
// original qualified name
226
272
let column = Column :: from_qualified_name ( field. name ( ) ) ;
227
273
match column {
228
274
Column {
229
- relation : Some ( r ) ,
275
+ relation : Some ( column_qualifier ) ,
230
276
name : column_name,
231
- } => & r == qq && column_name == name,
277
+ } => {
278
+ column_qualifier. resolved_eq ( qualifier) && column_name == name
279
+ }
232
280
_ => false ,
233
281
}
234
282
}
235
- // field to lookup is unqualified, no need to compare qualifier
236
- ( None , Some ( _) ) | ( None , None ) => field. name ( ) == name,
237
283
} )
238
284
. map ( |( idx, _) | idx) ;
239
285
Ok ( matches. next ( ) )
0 commit comments