@@ -44,6 +44,35 @@ fn parse_file_type(s: &str) -> Result<String, ParserError> {
44
44
Ok ( s. to_uppercase ( ) )
45
45
}
46
46
47
+ /// DataFusion specific EXPLAIN (needed so we can EXPLAIN datafusion
48
+ /// specific COPY and other statements)
49
+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
50
+ pub struct ExplainStatement {
51
+ pub analyze : bool ,
52
+ pub verbose : bool ,
53
+ pub statement : Box < Statement > ,
54
+ }
55
+
56
+ impl fmt:: Display for ExplainStatement {
57
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
58
+ let Self {
59
+ analyze,
60
+ verbose,
61
+ statement,
62
+ } = self ;
63
+
64
+ write ! ( f, "EXPLAIN " ) ?;
65
+ if * analyze {
66
+ write ! ( f, "ANALYZE " ) ?;
67
+ }
68
+ if * verbose {
69
+ write ! ( f, "VERBOSE " ) ?;
70
+ }
71
+
72
+ write ! ( f, "{statement}" )
73
+ }
74
+ }
75
+
47
76
/// DataFusion extension DDL for `COPY`
48
77
///
49
78
/// # Syntax:
@@ -74,7 +103,7 @@ pub struct CopyToStatement {
74
103
/// The URL to where the data is heading
75
104
pub target : String ,
76
105
/// Target specific options
77
- pub options : HashMap < String , Value > ,
106
+ pub options : Vec < ( String , Value ) > ,
78
107
}
79
108
80
109
impl fmt:: Display for CopyToStatement {
@@ -88,10 +117,8 @@ impl fmt::Display for CopyToStatement {
88
117
write ! ( f, "COPY {source} TO {target}" ) ?;
89
118
90
119
if !options. is_empty ( ) {
91
- let mut opts: Vec < _ > =
92
- options. iter ( ) . map ( |( k, v) | format ! ( "{k} {v}" ) ) . collect ( ) ;
120
+ let opts: Vec < _ > = options. iter ( ) . map ( |( k, v) | format ! ( "{k} {v}" ) ) . collect ( ) ;
93
121
// print them in sorted order
94
- opts. sort_unstable ( ) ;
95
122
write ! ( f, " ({})" , opts. join( ", " ) ) ?;
96
123
}
97
124
@@ -208,6 +235,8 @@ pub enum Statement {
208
235
DescribeTableStmt ( DescribeTableStmt ) ,
209
236
/// Extension: `COPY TO`
210
237
CopyTo ( CopyToStatement ) ,
238
+ /// EXPLAIN for extensions
239
+ Explain ( ExplainStatement ) ,
211
240
}
212
241
213
242
impl fmt:: Display for Statement {
@@ -217,11 +246,12 @@ impl fmt::Display for Statement {
217
246
Statement :: CreateExternalTable ( stmt) => write ! ( f, "{stmt}" ) ,
218
247
Statement :: DescribeTableStmt ( _) => write ! ( f, "DESCRIBE TABLE ..." ) ,
219
248
Statement :: CopyTo ( stmt) => write ! ( f, "{stmt}" ) ,
249
+ Statement :: Explain ( stmt) => write ! ( f, "{stmt}" ) ,
220
250
}
221
251
}
222
252
}
223
253
224
- /// DataFusion SQL Parser based on [`sqlparser`]
254
+ /// Datafusion SQL Parser based on [`sqlparser`]
225
255
///
226
256
/// Parses DataFusion's SQL dialect, often delegating to [`sqlparser`]'s
227
257
/// [`Parser`](sqlparser::parser::Parser).
@@ -307,24 +337,24 @@ impl<'a> DFParser<'a> {
307
337
Token :: Word ( w) => {
308
338
match w. keyword {
309
339
Keyword :: CREATE => {
310
- // move one token forward
311
- self . parser . next_token ( ) ;
312
- // use custom parsing
340
+ self . parser . next_token ( ) ; // CREATE
313
341
self . parse_create ( )
314
342
}
315
343
Keyword :: COPY => {
316
- // move one token forward
317
- self . parser . next_token ( ) ;
344
+ self . parser . next_token ( ) ; // COPY
318
345
self . parse_copy ( )
319
346
}
320
347
Keyword :: DESCRIBE => {
321
- // move one token forward
322
- self . parser . next_token ( ) ;
323
- // use custom parsing
348
+ self . parser . next_token ( ) ; // DESCRIBE
324
349
self . parse_describe ( )
325
350
}
351
+ Keyword :: EXPLAIN => {
352
+ // (TODO parse all supported statements)
353
+ self . parser . next_token ( ) ; // EXPLAIN
354
+ self . parse_explain ( )
355
+ }
326
356
_ => {
327
- // use the native parser
357
+ // use sqlparser-rs parser
328
358
Ok ( Statement :: Statement ( Box :: from (
329
359
self . parser . parse_statement ( ) ?,
330
360
) ) )
@@ -369,7 +399,7 @@ impl<'a> DFParser<'a> {
369
399
let options = if self . parser . peek_token ( ) . token == Token :: LParen {
370
400
self . parse_value_options ( ) ?
371
401
} else {
372
- HashMap :: new ( )
402
+ vec ! [ ]
373
403
} ;
374
404
375
405
Ok ( Statement :: CopyTo ( CopyToStatement {
@@ -421,6 +451,19 @@ impl<'a> DFParser<'a> {
421
451
}
422
452
}
423
453
454
+ /// Parse a SQL `EXPLAIN`
455
+ pub fn parse_explain ( & mut self ) -> Result < Statement , ParserError > {
456
+ let analyze = self . parser . parse_keyword ( Keyword :: ANALYZE ) ;
457
+ let verbose = self . parser . parse_keyword ( Keyword :: VERBOSE ) ;
458
+ let statement = self . parse_statement ( ) ?;
459
+
460
+ Ok ( Statement :: Explain ( ExplainStatement {
461
+ statement : Box :: new ( statement) ,
462
+ analyze,
463
+ verbose,
464
+ } ) )
465
+ }
466
+
424
467
/// Parse a SQL `CREATE` statement handling `CREATE EXTERNAL TABLE`
425
468
pub fn parse_create ( & mut self ) -> Result < Statement , ParserError > {
426
469
if self . parser . parse_keyword ( Keyword :: EXTERNAL ) {
@@ -758,14 +801,14 @@ impl<'a> DFParser<'a> {
758
801
/// Unlike [`Self::parse_string_options`], this method supports
759
802
/// keywords as key names as well as multiple value types such as
760
803
/// Numbers as well as Strings.
761
- fn parse_value_options ( & mut self ) -> Result < HashMap < String , Value > , ParserError > {
762
- let mut options = HashMap :: new ( ) ;
804
+ fn parse_value_options ( & mut self ) -> Result < Vec < ( String , Value ) > , ParserError > {
805
+ let mut options = vec ! [ ] ;
763
806
self . parser . expect_token ( & Token :: LParen ) ?;
764
807
765
808
loop {
766
809
let key = self . parse_option_key ( ) ?;
767
810
let value = self . parse_option_value ( ) ?;
768
- options. insert ( key, value) ;
811
+ options. push ( ( key, value) ) ;
769
812
let comma = self . parser . consume_token ( & Token :: Comma ) ;
770
813
if self . parser . consume_token ( & Token :: RParen ) {
771
814
// allow a trailing comma, even though it's not in standard
@@ -1285,13 +1328,39 @@ mod tests {
1285
1328
let expected = Statement :: CopyTo ( CopyToStatement {
1286
1329
source : object_name ( "foo" ) ,
1287
1330
target : "bar" . to_string ( ) ,
1288
- options : HashMap :: new ( ) ,
1331
+ options : vec ! [ ] ,
1289
1332
} ) ;
1290
1333
1291
1334
assert_eq ! ( verified_stmt( sql) , expected) ;
1292
1335
Ok ( ( ) )
1293
1336
}
1294
1337
1338
+ #[ test]
1339
+ fn explain_copy_to_table_to_table ( ) -> Result < ( ) , ParserError > {
1340
+ let cases = vec ! [
1341
+ ( "EXPLAIN COPY foo TO bar" , false , false ) ,
1342
+ ( "EXPLAIN ANALYZE COPY foo TO bar" , true , false ) ,
1343
+ ( "EXPLAIN VERBOSE COPY foo TO bar" , false , true ) ,
1344
+ ( "EXPLAIN ANALYZE VERBOSE COPY foo TO bar" , true , true ) ,
1345
+ ] ;
1346
+ for ( sql, analyze, verbose) in cases {
1347
+ println ! ( "sql: {sql}, analyze: {analyze}, verbose: {verbose}" ) ;
1348
+
1349
+ let expected_copy = Statement :: CopyTo ( CopyToStatement {
1350
+ source : object_name ( "foo" ) ,
1351
+ target : "bar" . to_string ( ) ,
1352
+ options : vec ! [ ] ,
1353
+ } ) ;
1354
+ let expected = Statement :: Explain ( ExplainStatement {
1355
+ analyze,
1356
+ verbose,
1357
+ statement : Box :: new ( expected_copy) ,
1358
+ } ) ;
1359
+ assert_eq ! ( verified_stmt( sql) , expected) ;
1360
+ }
1361
+ Ok ( ( ) )
1362
+ }
1363
+
1295
1364
#[ test]
1296
1365
fn copy_to_query_to_table ( ) -> Result < ( ) , ParserError > {
1297
1366
let statement = verified_stmt ( "SELECT 1" ) ;
@@ -1313,7 +1382,7 @@ mod tests {
1313
1382
let expected = Statement :: CopyTo ( CopyToStatement {
1314
1383
source : CopyToSource :: Query ( query) ,
1315
1384
target : "bar" . to_string ( ) ,
1316
- options : HashMap :: new ( ) ,
1385
+ options : vec ! [ ] ,
1317
1386
} ) ;
1318
1387
assert_eq ! ( verified_stmt( sql) , expected) ;
1319
1388
Ok ( ( ) )
@@ -1325,28 +1394,22 @@ mod tests {
1325
1394
let expected = Statement :: CopyTo ( CopyToStatement {
1326
1395
source : object_name ( "foo" ) ,
1327
1396
target : "bar" . to_string ( ) ,
1328
- options : HashMap :: from ( [ (
1397
+ options : vec ! [ (
1329
1398
"row_group_size" . to_string( ) ,
1330
1399
Value :: Number ( "55" . to_string( ) , false ) ,
1331
- ) ] ) ,
1400
+ ) ] ,
1332
1401
} ) ;
1333
1402
assert_eq ! ( verified_stmt( sql) , expected) ;
1334
1403
Ok ( ( ) )
1335
1404
}
1336
1405
1337
1406
#[ test]
1338
1407
fn copy_to_multi_options ( ) -> Result < ( ) , ParserError > {
1408
+ // order of options is preserved
1339
1409
let sql =
1340
1410
"COPY foo TO bar (format parquet, row_group_size 55, compression snappy)" ;
1341
- // canonical order is alphabetical
1342
- let canonical =
1343
- "COPY foo TO bar (compression snappy, format parquet, row_group_size 55)" ;
1344
1411
1345
- let expected_options = HashMap :: from ( [
1346
- (
1347
- "compression" . to_string ( ) ,
1348
- Value :: UnQuotedString ( "snappy" . to_string ( ) ) ,
1349
- ) ,
1412
+ let expected_options = vec ! [
1350
1413
(
1351
1414
"format" . to_string( ) ,
1352
1415
Value :: UnQuotedString ( "parquet" . to_string( ) ) ,
@@ -1355,14 +1418,17 @@ mod tests {
1355
1418
"row_group_size" . to_string( ) ,
1356
1419
Value :: Number ( "55" . to_string( ) , false ) ,
1357
1420
) ,
1358
- ] ) ;
1421
+ (
1422
+ "compression" . to_string( ) ,
1423
+ Value :: UnQuotedString ( "snappy" . to_string( ) ) ,
1424
+ ) ,
1425
+ ] ;
1359
1426
1360
- let options =
1361
- if let Statement :: CopyTo ( copy_to) = one_statement_parses_to ( sql, canonical) {
1362
- copy_to. options
1363
- } else {
1364
- panic ! ( "Expected copy" ) ;
1365
- } ;
1427
+ let options = if let Statement :: CopyTo ( copy_to) = verified_stmt ( sql) {
1428
+ copy_to. options
1429
+ } else {
1430
+ panic ! ( "Expected copy" ) ;
1431
+ } ;
1366
1432
1367
1433
assert_eq ! ( options, expected_options) ;
1368
1434
0 commit comments