@@ -94,16 +94,23 @@ impl ListingSchemaProvider {
94
94
let base = Path :: new ( self . path . as_ref ( ) ) ;
95
95
let mut tables = HashSet :: new ( ) ;
96
96
for file in entries. iter ( ) {
97
+ // The listing will initially be a file. However if we've recursed up to match our base, we know our path is a directory.
98
+ let mut is_dir = false ;
97
99
let mut parent = Path :: new ( file. location . as_ref ( ) ) ;
98
100
while let Some ( p) = parent. parent ( ) {
99
101
if p == base {
100
- tables. insert ( parent) ;
102
+ tables. insert ( TablePath {
103
+ is_dir,
104
+ path : parent,
105
+ } ) ;
101
106
}
102
107
parent = p;
108
+ is_dir = true ;
103
109
}
104
110
}
105
111
for table in tables. iter ( ) {
106
112
let file_name = table
113
+ . path
107
114
. file_name ( )
108
115
. ok_or_else ( || {
109
116
DataFusionError :: Internal ( "Cannot parse file name!" . to_string ( ) )
@@ -113,7 +120,7 @@ impl ListingSchemaProvider {
113
120
DataFusionError :: Internal ( "Cannot parse file name!" . to_string ( ) )
114
121
} ) ?;
115
122
let table_name = file_name. split ( '.' ) . collect_vec ( ) [ 0 ] ;
116
- let table_path = table. to_str ( ) . ok_or_else ( || {
123
+ let table_path = table. to_string ( ) . ok_or_else ( || {
117
124
DataFusionError :: Internal ( "Cannot parse file name!" . to_string ( ) )
118
125
} ) ?;
119
126
@@ -197,3 +204,48 @@ impl SchemaProvider for ListingSchemaProvider {
197
204
. contains_key ( name)
198
205
}
199
206
}
207
+
208
+ /// Stores metadata along with a table's path.
209
+ /// Primarily whether the path is a directory or not.
210
+ #[ derive( Eq , PartialEq , Hash , Debug ) ]
211
+ struct TablePath < ' a > {
212
+ path : & ' a Path ,
213
+ is_dir : bool ,
214
+ }
215
+
216
+ impl TablePath < ' _ > {
217
+ /// Format the path with a '/' appended if its a directory.
218
+ /// Clients (eg. object_store listing) can and will use the presence of trailing slash as a heuristic
219
+ fn to_string ( & self ) -> Option < String > {
220
+ self . path . to_str ( ) . map ( |path| {
221
+ if self . is_dir {
222
+ format ! ( "{path}/" )
223
+ } else {
224
+ path. to_string ( )
225
+ }
226
+ } )
227
+ }
228
+ }
229
+
230
+ #[ cfg( test) ]
231
+ mod tests {
232
+ use super :: * ;
233
+
234
+ #[ test]
235
+ fn table_path_ends_with_slash_when_is_dir ( ) {
236
+ let table_path = TablePath {
237
+ path : Path :: new ( "/file" ) ,
238
+ is_dir : true ,
239
+ } ;
240
+ assert ! ( table_path. to_string( ) . expect( "table path" ) . ends_with( "/" ) ) ;
241
+ }
242
+
243
+ #[ test]
244
+ fn dir_table_path_str_does_not_end_with_slash_when_not_is_dir ( ) {
245
+ let table_path = TablePath {
246
+ path : Path :: new ( "/file" ) ,
247
+ is_dir : false ,
248
+ } ;
249
+ assert ! ( !table_path. to_string( ) . expect( "table_path" ) . ends_with( "/" ) ) ;
250
+ }
251
+ }
0 commit comments