@@ -27,11 +27,14 @@ use std::sync::Arc;
27
27
28
28
/// A named reference to a qualified field in a schema.
29
29
#[ derive( Debug , Clone , PartialEq , Eq , Hash , PartialOrd , Ord ) ]
30
- pub struct Column {
31
- /// relation/table reference.
32
- pub relation : Option < OwnedTableReference > ,
33
- /// field/column name.
34
- pub name : String ,
30
+ pub enum Column {
31
+ Unqualified {
32
+ name : String ,
33
+ } ,
34
+ Qualified {
35
+ relation : OwnedTableReference ,
36
+ name : String ,
37
+ } ,
35
38
}
36
39
37
40
impl Column {
@@ -45,26 +48,23 @@ impl Column {
45
48
relation : Option < impl Into < OwnedTableReference > > ,
46
49
name : impl Into < String > ,
47
50
) -> Self {
48
- Self {
49
- relation : relation. map ( |r| r. into ( ) ) ,
50
- name : name. into ( ) ,
51
+ match relation {
52
+ Some ( relation) => Self :: Qualified {
53
+ relation : relation. into ( ) ,
54
+ name : name. into ( ) ,
55
+ } ,
56
+ None => Self :: Unqualified { name : name. into ( ) } ,
51
57
}
52
58
}
53
59
54
60
/// Convenience method for when there is no qualifier
55
61
pub fn new_unqualified ( name : impl Into < String > ) -> Self {
56
- Self {
57
- relation : None ,
58
- name : name. into ( ) ,
59
- }
62
+ Self :: Unqualified { name : name. into ( ) }
60
63
}
61
64
62
65
/// Create Column from unqualified name.
63
66
pub fn from_name ( name : impl Into < String > ) -> Self {
64
- Self {
65
- relation : None ,
66
- name : name. into ( ) ,
67
- }
67
+ Self :: Unqualified { name : name. into ( ) }
68
68
}
69
69
70
70
fn from_idents ( idents : & mut Vec < String > ) -> Option < Self > {
@@ -95,52 +95,81 @@ impl Column {
95
95
// identifiers will be treated as an unqualified column name
96
96
_ => return None ,
97
97
} ;
98
- Some ( Self { relation, name } )
98
+ match relation {
99
+ Some ( relation) => Some ( Self :: Qualified { relation, name } ) ,
100
+ None => Some ( Self :: Unqualified { name } ) ,
101
+ }
99
102
}
100
103
101
104
/// Deserialize a fully qualified name string into a column
102
105
///
103
106
/// Treats the name as a SQL identifier. For example
104
107
/// `foo.BAR` would be parsed to a reference to relation `foo`, column name `bar` (lower case)
105
108
/// where `"foo.BAR"` would be parsed to a reference to column named `foo.BAR`
106
- pub fn from_qualified_name ( flat_name : impl Into < String > ) -> Self {
107
- let flat_name: & str = & flat_name. into ( ) ;
108
- Self :: from_idents ( & mut parse_identifiers_normalized ( flat_name, false ) )
109
- . unwrap_or_else ( || Self {
110
- relation : None ,
111
- name : flat_name. to_owned ( ) ,
112
- } )
109
+ pub fn from_qualified_name ( qualified_name : impl Into < String > ) -> Self {
110
+ let qualified_name: & str = & qualified_name. into ( ) ;
111
+ Self :: from_idents ( & mut parse_identifiers_normalized ( qualified_name, false ) )
112
+ . unwrap_or_else ( || Self :: new_unqualified ( qualified_name. to_owned ( ) ) )
113
113
}
114
114
115
115
/// Deserialize a fully qualified name string into a column preserving column text case
116
- pub fn from_qualified_name_ignore_case ( flat_name : impl Into < String > ) -> Self {
117
- let flat_name: & str = & flat_name. into ( ) ;
118
- Self :: from_idents ( & mut parse_identifiers_normalized ( flat_name, true ) )
119
- . unwrap_or_else ( || Self {
120
- relation : None ,
121
- name : flat_name. to_owned ( ) ,
122
- } )
116
+ pub fn from_qualified_name_ignore_case ( qualified_name : impl Into < String > ) -> Self {
117
+ let qualified_name: & str = & qualified_name. into ( ) ;
118
+ Self :: from_idents ( & mut parse_identifiers_normalized ( qualified_name, true ) )
119
+ . unwrap_or_else ( || Self :: new_unqualified ( qualified_name. to_owned ( ) ) )
120
+ }
121
+
122
+ pub fn relation ( & self ) -> Option < & OwnedTableReference > {
123
+ match self {
124
+ Self :: Unqualified { .. } => None ,
125
+ Self :: Qualified { relation, .. } => Some ( relation) ,
126
+ }
127
+ }
128
+
129
+ pub fn unqualified_column ( & self ) -> Self {
130
+ Self :: Unqualified {
131
+ name : self . unqualified_name ( ) ,
132
+ }
133
+ }
134
+
135
+ pub fn with_new_qualifier (
136
+ & self ,
137
+ qualifier : Option < impl Into < OwnedTableReference > > ,
138
+ ) -> Self {
139
+ match self {
140
+ Self :: Unqualified { name } | Self :: Qualified { name, .. } => {
141
+ Self :: new ( qualifier, name)
142
+ }
143
+ }
144
+ }
145
+
146
+ /// Unqualified name string
147
+ pub fn unqualified_name ( & self ) -> String {
148
+ match self {
149
+ Self :: Unqualified { name } | Self :: Qualified { name, .. } => name. clone ( ) ,
150
+ }
151
+ }
152
+
153
+ /// Serialize column into a qualified name string
154
+ pub fn qualified_name ( & self ) -> String {
155
+ match self {
156
+ Self :: Unqualified { name } => name. clone ( ) ,
157
+ Self :: Qualified { relation, name } => format ! ( "{}.{}" , relation, name) ,
158
+ }
123
159
}
124
160
125
161
/// Serialize column into a flat name string
126
162
pub fn flat_name ( & self ) -> String {
127
- match & self . relation {
128
- Some ( r) => format ! ( "{}.{}" , r, self . name) ,
129
- None => self . name . clone ( ) ,
130
- }
163
+ self . qualified_name ( )
131
164
}
132
165
133
166
/// Serialize column into a quoted flat name string
134
167
pub fn quoted_flat_name ( & self ) -> String {
135
- match & self . relation {
136
- Some ( r) => {
137
- format ! (
138
- "{}.{}" ,
139
- r. to_quoted_string( ) ,
140
- quote_identifier( self . name. as_str( ) )
141
- )
168
+ match self {
169
+ Self :: Unqualified { name } => quote_identifier ( name) . to_string ( ) ,
170
+ Self :: Qualified { relation, name } => {
171
+ format ! ( "{}.{}" , relation. to_quoted_string( ) , quote_identifier( name) )
142
172
}
143
- None => quote_identifier ( & self . name ) . to_string ( ) ,
144
173
}
145
174
}
146
175
@@ -172,12 +201,12 @@ impl Column {
172
201
schemas : & [ & Arc < DFSchema > ] ,
173
202
using_columns : & [ HashSet < Column > ] ,
174
203
) -> Result < Self > {
175
- if self . relation . is_some ( ) {
204
+ if let Self :: Qualified { .. } = self {
176
205
return Ok ( self ) ;
177
206
}
178
207
179
208
for schema in schemas {
180
- let fields = schema. fields_with_unqualified_name ( & self . name ) ;
209
+ let fields = schema. fields_with_unqualified_name ( & self . unqualified_name ( ) ) ;
181
210
match fields. len ( ) {
182
211
0 => continue ,
183
212
1 => {
@@ -212,7 +241,7 @@ impl Column {
212
241
}
213
242
214
243
Err ( DataFusionError :: SchemaError ( SchemaError :: FieldNotFound {
215
- field : Box :: new ( Column :: new ( self . relation . clone ( ) , self . name ) ) ,
244
+ field : Box :: new ( self . clone ( ) ) ,
216
245
valid_fields : schemas
217
246
. iter ( )
218
247
. flat_map ( |s| s. fields ( ) . iter ( ) . map ( |f| f. qualified_column ( ) ) )
@@ -261,14 +290,14 @@ impl Column {
261
290
schemas : & [ & [ & DFSchema ] ] ,
262
291
using_columns : & [ HashSet < Column > ] ,
263
292
) -> Result < Self > {
264
- if self . relation . is_some ( ) {
293
+ if let Self :: Qualified { .. } = self {
265
294
return Ok ( self ) ;
266
295
}
267
296
268
297
for schema_level in schemas {
269
298
let fields = schema_level
270
299
. iter ( )
271
- . flat_map ( |s| s . fields_with_unqualified_name ( & self . name ) )
300
+ . flat_map ( |schema| schema . fields_with_unqualified_name ( & self . flat_name ( ) ) )
272
301
. collect :: < Vec < _ > > ( ) ;
273
302
match fields. len ( ) {
274
303
0 => continue ,
@@ -301,7 +330,7 @@ impl Column {
301
330
// If not due to USING columns then due to ambiguous column name
302
331
return Err ( DataFusionError :: SchemaError (
303
332
SchemaError :: AmbiguousReference {
304
- field : Column :: new_unqualified ( self . name ) ,
333
+ field : Column :: new_unqualified ( self . flat_name ( ) ) ,
305
334
} ,
306
335
) ) ;
307
336
}
0 commit comments