Skip to main content

qusql_parse/
show.rs

1use alloc::boxed::Box;
2
3use crate::{
4    SString, Span, Spanned, Statement,
5    expression::{Expression, PRIORITY_MAX, parse_expression_unreserved},
6    keywords::Keyword,
7    lexer::Token,
8    parser::{ParseError, Parser},
9    qualified_name::parse_qualified_name_unreserved,
10};
11
12/// Parse result for `SHOW TABLES` variants
13///
14/// ```
15/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
16/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
17/// let sql = "SHOW EXTENDED TABLES FROM test_db LIKE 't%';";
18/// let mut issues = Issues::new(sql);
19/// let mut stmts = parse_statements(sql, &mut issues, &options);
20/// # assert!(issues.is_ok(), "{}", issues);
21/// match stmts.pop() {
22///     Some(Statement::ShowTables(s)) => {
23///         // inspect s.extended, s.db, s.pattern, etc.
24///     }
25///     _ => panic!("expected ShowTables"),
26/// }
27/// ```
28#[derive(Clone, Debug)]
29pub struct ShowTables<'a> {
30    pub show_span: Span,
31    pub tables_span: Span,
32    pub extended: Option<Span>,
33    pub full: Option<Span>,
34    pub db: Option<crate::QualifiedName<'a>>,
35    pub like: Option<SString<'a>>,
36    pub where_expr: Option<Expression<'a>>,
37}
38
39impl<'a> Spanned for ShowTables<'a> {
40    fn span(&self) -> Span {
41        self.show_span
42            .join_span(&self.tables_span)
43            .join_span(&self.extended)
44            .join_span(&self.full)
45            .join_span(&self.db)
46            .join_span(&self.like)
47            .join_span(&self.where_expr)
48    }
49}
50
51fn parse_show_tables<'a>(
52    parser: &mut Parser<'a, '_>,
53    show_span: Span,
54    extended: Option<Span>,
55    full: Option<Span>,
56) -> Result<ShowTables<'a>, ParseError> {
57    let tables_span = parser.consume_keyword(Keyword::TABLES)?;
58
59    // optional FROM or IN db_name
60    let mut db = None;
61    match &parser.token {
62        Token::Ident(_, Keyword::FROM) => {
63            parser.consume_keyword(Keyword::FROM)?;
64            // Only restrict LIKE and WHERE, which can follow the db name
65            let q = parse_qualified_name_unreserved(parser)?;
66            db = Some(q);
67        }
68        Token::Ident(_, Keyword::IN) => {
69            parser.consume_keyword(Keyword::IN)?;
70            let q = parse_qualified_name_unreserved(parser)?;
71            db = Some(q);
72        }
73        _ => {}
74    }
75
76    // optional LIKE or WHERE
77    let like = if parser.skip_keyword(Keyword::LIKE).is_some() {
78        Some(parser.consume_string()?)
79    } else {
80        None
81    };
82    let where_expr = if like.is_none() && parser.skip_keyword(Keyword::WHERE).is_some() {
83        Some(parse_expression_unreserved(parser, PRIORITY_MAX)?)
84    } else {
85        None
86    };
87
88    Ok(ShowTables {
89        show_span,
90        tables_span,
91        extended,
92        full,
93        db,
94        like,
95        where_expr,
96    })
97}
98
99/// Parse result for `SHOW DATABASES`
100///
101/// ```
102/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
103/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
104/// let sql = "SHOW DATABASES;";
105/// let mut issues = Issues::new(sql);
106/// let mut stmts = parse_statements(sql, &mut issues, &options);
107/// # assert!(issues.is_ok(), "{}", issues);
108/// match stmts.pop() {
109///     Some(Statement::ShowDatabases(_)) => {}
110///     _ => panic!("expected ShowDatabases"),
111/// }
112/// ```
113#[derive(Clone, Debug)]
114pub struct ShowDatabases {
115    pub show_span: Span,
116    pub databases_span: Span,
117}
118
119impl Spanned for ShowDatabases {
120    fn span(&self) -> Span {
121        self.show_span.clone().join_span(&self.databases_span)
122    }
123}
124
125fn parse_show_databases<'a>(
126    parser: &mut Parser<'a, '_>,
127    show_span: Span,
128) -> Result<ShowDatabases, ParseError> {
129    let databases_span = parser.consume_keyword(Keyword::DATABASES)?;
130    Ok(ShowDatabases {
131        show_span,
132        databases_span,
133    })
134}
135
136/// Parse result for `SHOW PROCESSLIST` / `SHOW FULL PROCESSLIST`
137///
138/// ```
139/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
140/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
141/// let sql = "SHOW FULL PROCESSLIST;";
142/// let mut issues = Issues::new(sql);
143/// let mut stmts = parse_statements(sql, &mut issues, &options);
144/// # assert!(issues.is_ok(), "{}", issues);
145/// match stmts.pop() {
146///     Some(Statement::ShowProcessList(_)) => {}
147///     _ => panic!("expected ShowProcessList"),
148/// }
149/// ```
150
151#[derive(Clone, Debug)]
152pub struct ShowProcessList {
153    pub show_span: Span,
154    pub process_span: Span,
155}
156
157impl Spanned for ShowProcessList {
158    fn span(&self) -> Span {
159        self.show_span.clone().join_span(&self.process_span)
160    }
161}
162
163fn parse_show_processlist<'a>(
164    parser: &mut Parser<'a, '_>,
165    show_span: Span,
166    _full: Option<Span>,
167) -> Result<ShowProcessList, ParseError> {
168    match &parser.token {
169        Token::Ident(_, Keyword::PROCESSLIST) => {
170            let process_span = parser.consume_keyword(Keyword::PROCESSLIST)?;
171            Ok(ShowProcessList {
172                show_span,
173                process_span,
174            })
175        }
176        Token::Ident(_, Keyword::PROCESS) => {
177            let process_span = parser.consume_keyword(Keyword::PROCESS)?;
178            Ok(ShowProcessList {
179                show_span,
180                process_span,
181            })
182        }
183        _ => parser.expected_failure("'PROCESS' | 'PROCESSLIST'"),
184    }
185}
186
187/// Parse result for `SHOW VARIABLES`
188///
189/// ```
190/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
191/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
192/// let sql = "SHOW VARIABLES LIKE 'max_%';";
193/// let mut issues = Issues::new(sql);
194/// let mut stmts = parse_statements(sql, &mut issues, &options);
195/// # assert!(issues.is_ok(), "{}", issues);
196/// match stmts.pop() {
197///     Some(Statement::ShowVariables(s)) => {
198///         // s.pattern contains the LIKE expression
199///     }
200///     _ => panic!("expected ShowVariables"),
201/// }
202/// ```
203#[derive(Clone, Debug)]
204pub struct ShowVariables<'a> {
205    pub show_span: Span,
206    pub variables_span: Span,
207    pub global_span: Option<Span>,
208    pub session_span: Option<Span>,
209    pub like: Option<SString<'a>>,
210    pub where_expr: Option<Expression<'a>>,
211}
212
213impl<'a> Spanned for ShowVariables<'a> {
214    fn span(&self) -> Span {
215        self.show_span
216            .join_span(&self.variables_span)
217            .join_span(&self.global_span)
218            .join_span(&self.session_span)
219            .join_span(&self.like)
220            .join_span(&self.where_expr)
221    }
222}
223
224fn parse_show_variables<'a>(
225    parser: &mut Parser<'a, '_>,
226    show_span: Span,
227    global_span: Option<Span>,
228    session_span: Option<Span>,
229) -> Result<ShowVariables<'a>, ParseError> {
230    let variables_span = parser.consume_keyword(Keyword::VARIABLES)?;
231    let like = if parser.skip_keyword(Keyword::LIKE).is_some() {
232        Some(parser.consume_string()?)
233    } else {
234        None
235    };
236    let where_expr = if parser.skip_keyword(Keyword::WHERE).is_some() {
237        Some(parse_expression_unreserved(parser, PRIORITY_MAX)?)
238    } else {
239        None
240    };
241    Ok(ShowVariables {
242        show_span,
243        variables_span,
244        global_span,
245        session_span,
246        like,
247        where_expr,
248    })
249}
250
251/// Parse result for `SHOW STATUS`
252///
253/// ```
254/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
255/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
256/// let sql = "SHOW STATUS LIKE 'Threads%';";
257/// let mut issues = Issues::new(sql);
258/// let mut stmts = parse_statements(sql, &mut issues, &options);
259/// # assert!(issues.is_ok(), "{}", issues);
260/// match stmts.pop() {
261///     Some(Statement::ShowStatus(_)) => {}
262///     _ => panic!("expected ShowStatus"),
263/// }
264/// ```
265#[derive(Clone, Debug)]
266pub struct ShowStatus<'a> {
267    pub show_span: Span,
268    pub status_span: Span,
269    pub global_span: Option<Span>,
270    pub session_span: Option<Span>,
271    pub like: Option<SString<'a>>,
272    pub where_expr: Option<Expression<'a>>,
273}
274
275impl<'a> Spanned for ShowStatus<'a> {
276    fn span(&self) -> Span {
277        self.show_span
278            .join_span(&self.status_span)
279            .join_span(&self.like)
280            .join_span(&self.where_expr)
281    }
282}
283
284fn parse_show_status<'a>(
285    parser: &mut Parser<'a, '_>,
286    show_span: Span,
287    global_span: Option<Span>,
288    session_span: Option<Span>,
289) -> Result<ShowStatus<'a>, ParseError> {
290    let status_span = parser.consume_keyword(Keyword::STATUS)?;
291    let like = if parser.skip_keyword(Keyword::LIKE).is_some() {
292        Some(parser.consume_string()?)
293    } else {
294        None
295    };
296    let where_expr = if parser.skip_keyword(Keyword::WHERE).is_some() {
297        Some(parse_expression_unreserved(parser, PRIORITY_MAX)?)
298    } else {
299        None
300    };
301    Ok(ShowStatus {
302        show_span,
303        status_span,
304        global_span,
305        session_span,
306        like,
307        where_expr,
308    })
309}
310
311/// Parse result for `SHOW COLUMNS` / `SHOW FIELDS`
312///
313/// ```
314/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
315/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
316/// let sql = "SHOW COLUMNS FROM `my_table` LIKE 'id%';";
317/// let mut issues = Issues::new(sql);
318/// let mut stmts = parse_statements(sql, &mut issues, &options);
319/// # assert!(issues.is_ok(), "{}", issues);
320/// match stmts.pop() {
321///     Some(Statement::ShowColumns(c)) => {
322///         // c.table contains the table name
323///     }
324///     _ => panic!("expected ShowColumns"),
325/// }
326/// ```
327#[derive(Clone, Debug)]
328pub struct ShowColumns<'a> {
329    pub show_span: Span,
330    pub columns_span: Span,
331    pub extended: Option<Span>,
332    pub full: Option<Span>,
333    pub table: Option<crate::QualifiedName<'a>>,
334    pub db: Option<crate::QualifiedName<'a>>,
335    pub like: Option<SString<'a>>,
336    pub where_expr: Option<Expression<'a>>,
337}
338
339impl<'a> Spanned for ShowColumns<'a> {
340    fn span(&self) -> Span {
341        self.show_span
342            .join_span(&self.columns_span)
343            .join_span(&self.table)
344            .join_span(&self.db)
345            .join_span(&self.like)
346            .join_span(&self.where_expr)
347    }
348}
349
350fn parse_show_columns<'a>(
351    parser: &mut Parser<'a, '_>,
352    show_span: Span,
353    extended: Option<Span>,
354    full: Option<Span>,
355) -> Result<ShowColumns<'a>, ParseError> {
356    let columns_span = match &parser.token {
357        Token::Ident(_, Keyword::COLUMNS) => parser.consume_keyword(Keyword::COLUMNS)?,
358        _ => parser.consume_keyword(Keyword::FIELDS)?,
359    };
360    let mut table = None;
361    let mut db = None;
362    // Restrict LIKE and WHERE after table/db names
363    if parser.skip_keyword(Keyword::FROM).is_some() || parser.skip_keyword(Keyword::IN).is_some() {
364        let q = parse_qualified_name_unreserved(parser)?;
365        table = Some(q);
366    }
367    // optional second FROM/IN specifying database: SHOW COLUMNS FROM tbl FROM db
368    if table.is_some() {
369        match &parser.token {
370            Token::Ident(_, Keyword::FROM) => {
371                parser.consume_keyword(Keyword::FROM)?;
372                let q = parse_qualified_name_unreserved(parser)?;
373                db = Some(q);
374            }
375            Token::Ident(_, Keyword::IN) => {
376                parser.consume_keyword(Keyword::IN)?;
377                let q = parse_qualified_name_unreserved(parser)?;
378                db = Some(q);
379            }
380            _ => {}
381        }
382    }
383    let like = if parser.skip_keyword(Keyword::LIKE).is_some() {
384        Some(parser.consume_string()?)
385    } else {
386        None
387    };
388    let where_expr = if like.is_none() && parser.skip_keyword(Keyword::WHERE).is_some() {
389        Some(parse_expression_unreserved(parser, PRIORITY_MAX)?)
390    } else {
391        None
392    };
393    Ok(ShowColumns {
394        show_span,
395        columns_span,
396        extended,
397        full,
398        table,
399        db,
400        like,
401        where_expr,
402    })
403}
404
405/// Parse result for `SHOW CHARACTER SET` / `SHOW CHARSET`
406///
407/// ```
408/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
409/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
410/// let sql = "SHOW CHARACTER SET WHERE Charset LIKE 'utf%';";
411/// let mut issues = Issues::new(sql);
412/// let mut stmts = parse_statements(sql, &mut issues, &options);
413/// # assert!(issues.is_ok(), "{}", issues);
414/// match stmts.pop() {
415///     Some(Statement::ShowCharacterSet(s)) => {
416///         // s.where_expr contains the WHERE expression; s.pattern contains the LIKE pattern when used directly
417///     }
418///     _ => panic!("expected ShowCharacterSet"),
419/// }
420/// ```
421#[derive(Clone, Debug)]
422pub struct ShowCharacterSet<'a> {
423    pub show_span: Span,
424    pub character_span: Option<Span>,
425    pub set_span: Span,
426    pub like: Option<SString<'a>>,
427    pub where_expr: Option<Expression<'a>>,
428}
429
430impl<'a> Spanned for ShowCharacterSet<'a> {
431    fn span(&self) -> Span {
432        self.show_span
433            .join_span(&self.character_span)
434            .join_span(&self.set_span)
435            .join_span(&self.like)
436            .join_span(&self.where_expr)
437    }
438}
439
440fn parse_show_character_set<'a>(
441    parser: &mut Parser<'a, '_>,
442    show_span: Span,
443) -> Result<ShowCharacterSet<'a>, ParseError> {
444    // Accept either: SHOW CHARSET ...  or SHOW CHARACTER SET ...
445    let mut character_span: Option<Span> = None;
446    let set_span = match &parser.token {
447        Token::Ident(_, Keyword::CHARSET) => parser.consume_keyword(Keyword::CHARSET)?,
448        Token::Ident(_, Keyword::CHARACTER) => {
449            character_span = Some(parser.consume_keyword(Keyword::CHARACTER)?);
450            parser.consume_keyword(Keyword::SET)?
451        }
452        _ => return parser.expected_failure("'CHARSET' | 'CHARACTER'"),
453    };
454
455    let mut like: Option<SString<'a>> = None;
456    let mut where_expr: Option<Expression<'a>> = None;
457    if parser.skip_keyword(Keyword::LIKE).is_some() {
458        like = Some(parser.consume_string()?);
459    } else if parser.skip_keyword(Keyword::WHERE).is_some() {
460        where_expr = Some(parse_expression_unreserved(parser, PRIORITY_MAX)?);
461    }
462
463    Ok(ShowCharacterSet {
464        show_span,
465        character_span,
466        set_span,
467        like,
468        where_expr,
469    })
470}
471
472/// Parse result for `SHOW CREATE TABLE`
473///
474/// ```
475/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
476/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
477/// let sql = "SHOW CREATE TABLE my_table;";
478/// let mut issues = Issues::new(sql);
479/// let mut stmts = parse_statements(sql, &mut issues, &options);
480/// # assert!(issues.is_ok(), "{}", issues);
481/// match stmts.pop() {
482///     Some(Statement::ShowCreateTable(s)) => {
483///         // s.table contains the table name
484///     }
485///     _ => panic!("expected ShowCreateTable"),
486/// }
487/// ```
488#[derive(Clone, Debug)]
489pub struct ShowCreateTable<'a> {
490    pub show_span: Span,
491    pub create_span: Span,
492    pub object_span: Span,
493    pub table: crate::QualifiedName<'a>,
494}
495
496impl<'a> Spanned for ShowCreateTable<'a> {
497    fn span(&self) -> Span {
498        self.show_span
499            .join_span(&self.create_span)
500            .join_span(&self.object_span)
501            .join_span(&self.table)
502    }
503}
504
505/// Parse result for `SHOW CREATE DATABASE`
506///
507/// ```
508/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
509/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
510/// let sql = "SHOW CREATE DATABASE my_db;";
511/// let mut issues = Issues::new(sql);
512/// let mut stmts = parse_statements(sql, &mut issues, &options);
513/// # assert!(issues.is_ok(), "{}", issues);
514/// match stmts.pop() {
515///     Some(Statement::ShowCreateDatabase(s)) => {
516///         // s.db contains the database name
517///     }
518///     _ => panic!("expected ShowCreateDatabase"),
519/// }
520/// ```
521#[derive(Clone, Debug)]
522pub struct ShowCreateDatabase<'a> {
523    pub show_span: Span,
524    pub create_span: Span,
525    pub object_span: Span,
526    pub db: crate::QualifiedName<'a>,
527}
528
529impl<'a> Spanned for ShowCreateDatabase<'a> {
530    fn span(&self) -> Span {
531        self.show_span
532            .join_span(&self.create_span)
533            .join_span(&self.object_span)
534            .join_span(&self.db)
535    }
536}
537
538/// Parse result for `SHOW CREATE VIEW`
539///
540/// ```
541/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
542/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
543/// let sql = "SHOW CREATE VIEW my_view;";
544/// let mut issues = Issues::new(sql);
545/// let mut stmts = parse_statements(sql, &mut issues, &options);
546/// # assert!(issues.is_ok(), "{}", issues);
547/// match stmts.pop() {
548///     Some(Statement::ShowCreateView(s)) => {
549///         // s.view contains the view name
550///     }
551///     _ => panic!("expected ShowCreateView"),
552/// }
553/// ```
554#[derive(Clone, Debug)]
555pub struct ShowCreateView<'a> {
556    pub show_span: Span,
557    pub create_span: Span,
558    pub object_span: Span,
559    pub view: crate::QualifiedName<'a>,
560}
561
562impl<'a> Spanned for ShowCreateView<'a> {
563    fn span(&self) -> Span {
564        self.show_span
565            .join_span(&self.create_span)
566            .join_span(&self.object_span)
567            .join_span(&self.view)
568    }
569}
570
571fn parse_show_create<'a>(
572    parser: &mut Parser<'a, '_>,
573    show_span: Span,
574) -> Result<crate::Statement<'a>, ParseError> {
575    let create_span = parser.consume_keyword(Keyword::CREATE)?;
576    match &parser.token {
577        Token::Ident(_, Keyword::TABLE) => {
578            let object_span = parser.consume_keyword(Keyword::TABLE)?;
579            let table = parse_qualified_name_unreserved(parser)?;
580            Ok(crate::Statement::ShowCreateTable(Box::new(
581                ShowCreateTable {
582                    show_span,
583                    create_span,
584                    object_span,
585                    table,
586                },
587            )))
588        }
589        Token::Ident(_, Keyword::DATABASE) => {
590            let object_span = parser.consume_keyword(Keyword::DATABASE)?;
591            let db = parse_qualified_name_unreserved(parser)?;
592            Ok(crate::Statement::ShowCreateDatabase(Box::new(
593                ShowCreateDatabase {
594                    show_span,
595                    create_span,
596                    object_span,
597                    db,
598                },
599            )))
600        }
601        Token::Ident(_, Keyword::VIEW) => {
602            let object_span = parser.consume_keyword(Keyword::VIEW)?;
603            let view = parse_qualified_name_unreserved(parser)?;
604            Ok(crate::Statement::ShowCreateView(Box::new(ShowCreateView {
605                show_span,
606                create_span,
607                object_span,
608                view,
609            })))
610        }
611        _ => parser.expected_failure("'TABLE' | 'DATABASE' | 'VIEW'"),
612    }
613}
614
615/// Parse result for `SHOW COLLATION`
616///
617/// ```
618/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
619/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
620/// let sql = "SHOW COLLATION LIKE 'utf%';";
621/// let mut issues = Issues::new(sql);
622/// let mut stmts = parse_statements(sql, &mut issues, &options);
623/// # assert!(issues.is_ok(), "{}", issues);
624/// match stmts.pop() {
625///     Some(Statement::ShowCollation(s)) => {
626///         // s.pattern contains the LIKE string
627///     }
628///     _ => panic!("expected ShowCollation"),
629/// }
630/// ```
631#[derive(Clone, Debug)]
632pub struct ShowCollation<'a> {
633    pub show_span: Span,
634    pub collation_span: Span,
635    pub like: Option<SString<'a>>,
636    pub where_expr: Option<Expression<'a>>,
637}
638
639impl<'a> Spanned for ShowCollation<'a> {
640    fn span(&self) -> Span {
641        self.show_span
642            .join_span(&self.collation_span)
643            .join_span(&self.like)
644            .join_span(&self.where_expr)
645    }
646}
647
648fn parse_show_collation<'a>(
649    parser: &mut Parser<'a, '_>,
650    show_span: Span,
651) -> Result<ShowCollation<'a>, ParseError> {
652    let collation_span = parser.consume_keyword(Keyword::COLLATION)?;
653    let mut like: Option<SString<'a>> = None;
654    let mut where_expr: Option<Expression<'a>> = None;
655    if parser.skip_keyword(Keyword::LIKE).is_some() {
656        like = Some(parser.consume_string()?);
657    } else if parser.skip_keyword(Keyword::WHERE).is_some() {
658        where_expr = Some(parse_expression_unreserved(parser, PRIORITY_MAX)?);
659    }
660    Ok(ShowCollation {
661        show_span,
662        collation_span,
663        like,
664        where_expr,
665    })
666}
667
668/// Parse result for `SHOW ENGINES`
669///
670/// ```
671/// # use qusql_parse::{SQLDialect, SQLArguments, ParseOptions, parse_statements, Statement, Issues};
672/// # let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
673/// let sql = "SHOW ENGINES;";
674/// let mut issues = Issues::new(sql);
675/// let mut stmts = parse_statements(sql, &mut issues, &options);
676/// # assert!(issues.is_ok(), "{}", issues);
677/// match stmts.pop() {
678///     Some(Statement::ShowEngines(s)) => {
679///         // s.engines_span is present
680///     }
681///     _ => panic!("expected ShowEngines"),
682/// }
683/// ```
684#[derive(Clone, Debug)]
685pub struct ShowEngines {
686    pub show_span: Span,
687    pub engines_span: Span,
688}
689
690impl Spanned for ShowEngines {
691    fn span(&self) -> Span {
692        self.show_span.join_span(&self.engines_span)
693    }
694}
695
696fn parse_show_engines<'a>(
697    parser: &mut Parser<'a, '_>,
698    show_span: Span,
699) -> Result<ShowEngines, ParseError> {
700    let engines_span = parser.consume_keyword(Keyword::ENGINES)?;
701    Ok(ShowEngines {
702        show_span,
703        engines_span,
704    })
705}
706
707pub(crate) fn parse_show<'a>(
708    parser: &mut Parser<'a, '_>,
709) -> Result<crate::Statement<'a>, ParseError> {
710    let show_span = parser.consume_keyword(Keyword::SHOW)?;
711    // parse optional modifiers EXTENDED, FULL, GLOBAL and SESSION (either or both) before dispatch
712    let mut extended: Option<Span> = None;
713    let mut full: Option<Span> = None;
714    let mut global: Option<Span> = None;
715    let mut session: Option<Span> = None;
716    loop {
717        match &parser.token {
718            Token::Ident(_, Keyword::EXTENDED) => extended = Some(parser.consume()),
719            Token::Ident(_, Keyword::FULL) => full = Some(parser.consume()),
720            Token::Ident(_, Keyword::GLOBAL) => global = Some(parser.consume()),
721            Token::Ident(_, Keyword::SESSION) => session = Some(parser.consume()),
722            _ => break,
723        }
724    }
725
726    let stmt = match &parser.token {
727        Token::Ident(_, Keyword::TABLES) => Statement::ShowTables(Box::new(parse_show_tables(parser, show_span, extended.clone(), full.clone())?)),
728        Token::Ident(_, Keyword::CREATE) => parse_show_create(parser, show_span)?,
729        Token::Ident(_, Keyword::DATABASES) => Statement::ShowDatabases(Box::new(parse_show_databases(parser, show_span)?)),
730        Token::Ident(_, Keyword::PROCESSLIST | Keyword::PROCESS) => {
731            Statement::ShowProcessList(Box::new(parse_show_processlist(parser, show_span, full.clone())?))
732        }
733        Token::Ident(_, Keyword::VARIABLES) => Statement::ShowVariables(Box::new(parse_show_variables(parser, show_span, global.clone(), session.clone())?)),
734        Token::Ident(_, Keyword::STATUS) => Statement::ShowStatus(Box::new(parse_show_status(parser, show_span, global.clone(), session.clone())?)),
735        Token::Ident(_, Keyword::COLUMNS | Keyword::FIELDS) => {
736            Statement::ShowColumns(Box::new(parse_show_columns(parser, show_span, extended.clone(), full.clone())?))
737        }
738        Token::Ident(_, Keyword::CHARSET | Keyword::CHARACTER) => {
739            Statement::ShowCharacterSet(Box::new(parse_show_character_set(parser, show_span)?))
740        }
741            Token::Ident(_, Keyword::COLLATION) => Statement::ShowCollation(Box::new(parse_show_collation(parser, show_span)?)),
742            Token::Ident(_, Keyword::ENGINES) => Statement::ShowEngines(Box::new(parse_show_engines(parser, show_span)?)),
743        _ if parser.options.dialect.is_postgresql() => {
744            // PostgreSQL: SHOW <parameter_name> — consume the parameter name as-is
745            let var_span = parser.consume_plain_identifier_unreserved()?;
746            Statement::ShowVariables(Box::new(ShowVariables {
747                show_span: show_span.clone(),
748                variables_span: var_span.span(),
749                global_span: global.clone(),
750                session_span: session.clone(),
751                like: None,
752                where_expr: None,
753            }))
754        }
755        _ => return parser.expected_failure("'TABLES' | 'DATABASES' | 'PROCESS' | 'PROCESSLIST' | 'VARIABLES' | 'STATUS' | 'COLUMNS' | 'FIELDS' | 'CHARSET' | 'CHARACTER' | 'COLLATION' | 'ENGINES'"),
756    };
757
758    // Emit warnings for modifiers not supported by the particular SHOW variant
759    if let Some(span) = &extended {
760        match &stmt {
761            crate::Statement::ShowTables(_) => {}
762            crate::Statement::ShowColumns(_) => {}
763            _ => {
764                parser.warn(
765                    "Modifier EXTENDED not supported for this SHOW variant",
766                    span,
767                );
768            }
769        }
770    }
771
772    if let Some(span) = &full {
773        match &stmt {
774            crate::Statement::ShowTables(_) => {}
775            crate::Statement::ShowProcessList(_) => {}
776            crate::Statement::ShowColumns(_) => {}
777            _ => {
778                parser.warn("Modifier FULL not supported for this SHOW variant", span);
779            }
780        }
781    }
782
783    if let Some(span) = &global {
784        match &stmt {
785            crate::Statement::ShowStatus(_) => {}
786            crate::Statement::ShowVariables(_) => {}
787            _ => {
788                parser.warn("Modifier GLOBAL not supported for this SHOW variant", span);
789            }
790        }
791    }
792
793    if let Some(span) = &session {
794        match &stmt {
795            crate::Statement::ShowStatus(_) => {}
796            crate::Statement::ShowVariables(_) => {}
797            _ => {
798                parser.warn("Modifier SESSION not supported for this SHOW variant", span);
799            }
800        }
801    }
802
803    Ok(stmt)
804}