iffyio commented on code in PR #1501:
URL:
https://github.com/apache/datafusion-sqlparser-rs/pull/1501#discussion_r1833098300
##########
src/parser/mod.rs:
##########
@@ -9685,58 +9711,95 @@ impl<'a> Parser<'a> {
extended: bool,
full: bool,
) -> Result<Statement, ParserError> {
- self.expect_one_of_keywords(&[Keyword::FROM, Keyword::IN])?;
- let object_name = self.parse_object_name(false)?;
- let table_name = match self.parse_one_of_keywords(&[Keyword::FROM,
Keyword::IN]) {
- Some(_) => {
- let db_name = vec![self.parse_identifier(false)?];
- let ObjectName(table_name) = object_name;
- let object_name =
db_name.into_iter().chain(table_name).collect();
- ObjectName(object_name)
- }
- None => object_name,
- };
- let filter = self.parse_show_statement_filter()?;
+ let filter;
+ let filter_position;
+ let show_in;
+ if self.dialect.supports_show_like_before_in() {
+ filter = self.parse_show_statement_filter()?;
+ filter_position = ShowStatementFilterPosition::InTheMiddle;
+ show_in = self.parse_show_opt_in()?;
+ } else {
+ show_in = self.parse_show_opt_in()?;
+ filter = self.parse_show_statement_filter()?;
+ filter_position = ShowStatementFilterPosition::AtTheEnd;
+ }
Ok(Statement::ShowColumns {
extended,
full,
- table_name,
+ show_in,
filter,
+ filter_position,
})
}
- pub fn parse_show_tables(
+ fn parse_show_tables(
&mut self,
+ terse: bool,
extended: bool,
full: bool,
+ external: bool,
) -> Result<Statement, ParserError> {
- let (clause, db_name) = match
self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) {
- Some(Keyword::FROM) => (Some(ShowClause::FROM),
Some(self.parse_identifier(false)?)),
- Some(Keyword::IN) => (Some(ShowClause::IN),
Some(self.parse_identifier(false)?)),
- _ => (None, None),
- };
- let filter = self.parse_show_statement_filter()?;
+ let history = !external && self.parse_keyword(Keyword::HISTORY);
+ let filter;
+ let show_in;
+ let filter_position;
+ if self.dialect.supports_show_like_before_in() {
+ filter = self.parse_show_statement_filter()?;
+ //YOAV: here we have a problem, the hint is DB-dependent (table in
a schemas or some other object)
Review Comment:
is the comment still valid or the issue was resolved?
##########
src/ast/mod.rs:
##########
@@ -4382,79 +4406,226 @@ impl fmt::Display for Statement {
Statement::ShowColumns {
extended,
full,
- table_name,
+ show_in,
filter,
+ filter_position,
} => {
write!(
f,
- "SHOW {extended}{full}COLUMNS FROM {table_name}",
+ "SHOW {extended}{full}COLUMNS",
extended = if *extended { "EXTENDED " } else { "" },
full = if *full { "FULL " } else { "" },
- table_name = table_name,
)?;
- if let Some(filter) = filter {
- write!(f, " {filter}")?;
+ if filter_position ==
&ShowStatementFilterPosition::InTheMiddle {
+ if let Some(filter) = filter {
+ write!(f, " {filter}")?;
+ }
+ if let Some(show_in) = show_in {
+ write!(f, " {show_in}")?;
+ }
+ }
+ if filter_position == &ShowStatementFilterPosition::AtTheEnd {
+ if let Some(show_in) = show_in {
+ write!(f, " {show_in}")?;
+ }
+ if let Some(filter) = filter {
+ write!(f, " {filter}")?;
+ }
}
Ok(())
}
- Statement::ShowDatabases { filter } => {
- write!(f, "SHOW DATABASES")?;
- if let Some(filter) = filter {
- write!(f, " {filter}")?;
- }
+ Statement::ShowDatabases {
+ terse,
+ history,
+ filter,
+ show_in,
+ starts_with,
+ limit,
+ from,
+ } => {
+ write!(
+ f,
+ "SHOW {terse}DATABASES",
+ terse = if *terse { "TERSE " } else { "" }
+ )?;
+ write!(
+ f,
+ "{history}{filter}{show_in}{starts_with}{limit}{from}",
+ history = if *history { " HISTORY" } else { "" },
+ filter = match filter.as_ref() {
+ Some(l) => format!(" {l}"),
+ None => String::new(),
+ },
+ show_in = match show_in {
+ Some(i) => format!(" {i}"),
+ None => String::new(),
+ },
+ starts_with = match starts_with.as_ref() {
+ Some(s) => format!(" STARTS WITH {s}"),
+ None => String::new(),
+ },
+ limit = match limit.as_ref() {
+ Some(l) => format!(" LIMIT {l}"),
+ None => String::new(),
+ },
+ from = match from.as_ref() {
+ Some(f) => format!(" FROM {f}"),
+ None => String::new(),
+ }
+ )?;
Ok(())
}
- Statement::ShowSchemas { filter } => {
- write!(f, "SHOW SCHEMAS")?;
- if let Some(filter) = filter {
- write!(f, " {filter}")?;
- }
+ Statement::ShowSchemas {
+ terse,
+ history,
+ filter,
+ show_in,
+ starts_with,
+ limit,
+ from,
+ } => {
+ write!(
+ f,
+ "SHOW {terse}SCHEMAS",
+ terse = if *terse { "TERSE " } else { "" }
+ )?;
+ write!(
+ f,
+ "{history}{filter}{show_in}{starts_with}{limit}{from}",
+ history = if *history { " HISTORY" } else { "" },
+ filter = match filter.as_ref() {
+ Some(l) => format!(" {l}"),
+ None => String::new(),
+ },
+ show_in = match show_in {
+ Some(i) => format!(" {i}"),
+ None => String::new(),
+ },
+ starts_with = match starts_with.as_ref() {
+ Some(s) => format!(" STARTS WITH {s}"),
+ None => String::new(),
+ },
+ limit = match limit.as_ref() {
+ Some(l) => format!(" LIMIT {l}"),
+ None => String::new(),
+ },
+ from = match from.as_ref() {
+ Some(f) => format!(" FROM {f}"),
+ None => String::new(),
+ }
+ )?;
Ok(())
}
Statement::ShowTables {
+ terse,
+ history,
extended,
full,
- clause: show_clause,
- db_name,
+ external,
filter,
+ show_in,
+ starts_with,
+ limit,
+ from,
+ filter_position,
} => {
write!(
f,
- "SHOW {extended}{full}TABLES",
+ "SHOW {terse}{extended}{full}{external}TABLES",
+ terse = if *terse { "TERSE " } else { "" },
extended = if *extended { "EXTENDED " } else { "" },
full = if *full { "FULL " } else { "" },
+ external = if *external { "EXTERNAL " } else { "" },
)?;
- if let Some(show_clause) = show_clause {
- write!(f, " {show_clause}")?;
- }
- if let Some(db_name) = db_name {
- write!(f, " {db_name}")?;
- }
- if let Some(filter) = filter {
- write!(f, " {filter}")?;
- }
- Ok(())
+ write!(
+ f,
+
"{history}{like_in_the_middle}{show_in}{starts_with}{limit}{from}{like_at_the_end}",
+ history = if *history { " HISTORY" } else { "" },
+ like_in_the_middle = if *filter_position
+ == ShowStatementFilterPosition::InTheMiddle
+ && filter.is_some()
+ {
+ format!(" {}", filter.as_ref().unwrap())
+ } else {
+ String::new()
+ },
+ show_in = match show_in {
+ Some(i) => format!(" {i}"),
+ None => String::new(),
+ },
+ starts_with = match starts_with.as_ref() {
+ Some(s) => format!(" STARTS WITH {s}"),
+ None => String::new(),
+ },
+ limit = match limit.as_ref() {
+ Some(l) => format!(" LIMIT {l}"),
+ None => String::new(),
+ },
+ from = match from.as_ref() {
+ Some(f) => format!(" FROM {f}"),
+ None => String::new(),
+ },
+ like_at_the_end = if *filter_position
+ == ShowStatementFilterPosition::AtTheEnd
+ && filter.is_some()
+ {
+ format!(" {}", filter.as_ref().unwrap())
+ } else {
+ String::new()
+ },
+ )
}
Statement::ShowViews {
+ terse,
materialized,
- clause: show_clause,
- db_name,
filter,
+ show_in,
+ starts_with,
+ limit,
+ from,
+ filter_position,
} => {
write!(
f,
- "SHOW {}VIEWS",
- if *materialized { "MATERIALIZED " } else { "" }
+ "SHOW {terse}{materialized}VIEWS",
+ terse = if *terse { "TERSE " } else { "" },
+ materialized = if *materialized { "MATERIALIZED " } else {
"" }
+ )?;
+ write!(
+ f,
+
"{like_in_the_middle}{show_in}{starts_with}{limit}{from}{like_at_the_end}",
+ like_in_the_middle = if *filter_position
+ == ShowStatementFilterPosition::InTheMiddle
+ && filter.is_some()
+ {
+ format!(" {}", filter.as_ref().unwrap())
+ } else {
+ String::new()
+ },
Review Comment:
one way I'm thinking might be able to reuse the same check for the
`like_in_the_middle` and `like_and_the_end` we could do e.g?
```rust
let (like_in_the_middle, like_in_the_end) = match filter_position {
ShowStatementFilterPosition::InTheMiddle => (filter.unwrap_or_default(),
"".to_string())
ShowStatementFilterPosition::AtTheEnd => ("".to_string(),
filter.unwrap_or_default())
};
write!(f,"...{like_in_the_middle}...{like_at_the_end)")
```
##########
src/ast/mod.rs:
##########
@@ -7345,6 +7516,55 @@ impl Display for UtilityOption {
}
}
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+pub enum ShowStatementFilterPosition {
Review Comment:
It looks like it's being used in tandem with `ShowStatementFilter`? If so we
can probably include the filter as part of the enum
```rust
enum ShowStatementFilterPosition {
InTheMiddle(ShowStatementFilter)
AtTheEnd(ShowStatementFilter)
}
```
That would leverage the typesystem so that they don't go out of sync, and
we'd avoid the `filter.is_some() && filter.unwrap()`.
Also maybe the variants could be Infix/Suffix for InTheMiddle/AtTheEnd
repectively if that would be reasonable in this context.
##########
src/parser/mod.rs:
##########
@@ -12289,6 +12352,137 @@ impl<'a> Parser<'a> {
}
false
}
+
+ /// Look for an expected keyword, without consuming it
+ fn peek_keyword(&self, expected: Keyword) -> bool {
+ match self.peek_token().token {
+ Token::Word(w) => expected == w.keyword,
+ _ => false,
+ }
+ }
+
+ /// Look for one of expected keyword, without consuming it
+ fn peek_keywords(&self, expected: &[Keyword]) -> bool {
+ for kw in expected {
+ if self.peek_keyword(*kw) {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn parse_show_opt_in(&mut self) -> Result<Option<ShowStatementIn>,
ParserError> {
+ let clause = match self.parse_one_of_keywords(&[Keyword::FROM,
Keyword::IN]) {
+ Some(Keyword::FROM) => ShowStatementInClause::FROM,
+ Some(Keyword::IN) => ShowStatementInClause::IN,
+ _ => return Ok(None),
+ };
+
+ if self.parse_keyword(Keyword::DATABASE) {
+ if self.peek_keywords(&[Keyword::STARTS, Keyword::WITH]) {
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Database),
+ parent_name: None,
+ }))
+ } else {
+ let parent_name = match self.parse_object_name(false) {
Review Comment:
```suggestion
let parent_name = match self.parse_object_name(false)?;
```
we would want to return the error if there was one?
##########
src/parser/mod.rs:
##########
@@ -12289,6 +12352,137 @@ impl<'a> Parser<'a> {
}
false
}
+
+ /// Look for an expected keyword, without consuming it
+ fn peek_keyword(&self, expected: Keyword) -> bool {
+ match self.peek_token().token {
+ Token::Word(w) => expected == w.keyword,
+ _ => false,
+ }
+ }
+
+ /// Look for one of expected keyword, without consuming it
+ fn peek_keywords(&self, expected: &[Keyword]) -> bool {
+ for kw in expected {
+ if self.peek_keyword(*kw) {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn parse_show_opt_in(&mut self) -> Result<Option<ShowStatementIn>,
ParserError> {
+ let clause = match self.parse_one_of_keywords(&[Keyword::FROM,
Keyword::IN]) {
+ Some(Keyword::FROM) => ShowStatementInClause::FROM,
+ Some(Keyword::IN) => ShowStatementInClause::IN,
+ _ => return Ok(None),
Review Comment:
we can return an error with e.g. `self.expected(...)` instead? since this
would be a bug on our end if we hit the branch, so that the parser doesn't
continue given it already consumed some tokens
##########
src/ast/mod.rs:
##########
@@ -7345,6 +7516,55 @@ impl Display for UtilityOption {
}
}
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+pub enum ShowStatementFilterPosition {
+ InTheMiddle, // Snowflake like
+ AtTheEnd, // MySQL like
Review Comment:
Can we give an example sql for both, showing the implied positions (fearing
that 'snowflake like' wouldn't be descriptive enough)?
##########
src/parser/mod.rs:
##########
@@ -12289,6 +12352,137 @@ impl<'a> Parser<'a> {
}
false
}
+
+ /// Look for an expected keyword, without consuming it
+ fn peek_keyword(&self, expected: Keyword) -> bool {
+ match self.peek_token().token {
+ Token::Word(w) => expected == w.keyword,
+ _ => false,
+ }
+ }
+
+ /// Look for one of expected keyword, without consuming it
+ fn peek_keywords(&self, expected: &[Keyword]) -> bool {
+ for kw in expected {
+ if self.peek_keyword(*kw) {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn parse_show_opt_in(&mut self) -> Result<Option<ShowStatementIn>,
ParserError> {
Review Comment:
would it make sense to call this `maybe_parse_show_in`?
##########
src/ast/mod.rs:
##########
@@ -2772,41 +2772,65 @@ pub enum Statement {
/// ```sql
/// SHOW COLUMNS
/// ```
- ///
- /// Note: this is a MySQL-specific statement.
ShowColumns {
extended: bool,
full: bool,
- #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
- table_name: ObjectName,
+ show_in: Option<ShowStatementIn>,
filter: Option<ShowStatementFilter>,
+ filter_position: ShowStatementFilterPosition,
},
/// ```sql
- /// SHOW DATABASES [LIKE 'pattern']
+ /// SHOW DATABASES
/// ```
- ShowDatabases { filter: Option<ShowStatementFilter> },
+ ShowDatabases {
+ terse: bool,
+ history: bool,
+ filter: Option<ShowStatementFilter>,
+ show_in: Option<ShowStatementIn>,
+ starts_with: Option<Value>,
+ limit: Option<Expr>,
+ from: Option<Value>,
Review Comment:
would it make sense to group these params into a dedicated type and reuse
that across the Show statements? Thinking that would also make it easier to
reuse the same parser code, as well as the display code for those fields in the
different statements
##########
src/parser/mod.rs:
##########
@@ -12289,6 +12352,137 @@ impl<'a> Parser<'a> {
}
false
}
+
+ /// Look for an expected keyword, without consuming it
+ fn peek_keyword(&self, expected: Keyword) -> bool {
+ match self.peek_token().token {
+ Token::Word(w) => expected == w.keyword,
+ _ => false,
+ }
+ }
+
+ /// Look for one of expected keyword, without consuming it
+ fn peek_keywords(&self, expected: &[Keyword]) -> bool {
Review Comment:
```suggestion
fn peek_one_of_keywords(&self, expected: &[Keyword]) -> bool {
```
since parse_keywords might instead suggest that we expect to parse the
specified keywords in sequence
##########
src/parser/mod.rs:
##########
@@ -12289,6 +12352,137 @@ impl<'a> Parser<'a> {
}
false
}
+
+ /// Look for an expected keyword, without consuming it
+ fn peek_keyword(&self, expected: Keyword) -> bool {
+ match self.peek_token().token {
+ Token::Word(w) => expected == w.keyword,
+ _ => false,
+ }
+ }
+
+ /// Look for one of expected keyword, without consuming it
+ fn peek_keywords(&self, expected: &[Keyword]) -> bool {
+ for kw in expected {
+ if self.peek_keyword(*kw) {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn parse_show_opt_in(&mut self) -> Result<Option<ShowStatementIn>,
ParserError> {
+ let clause = match self.parse_one_of_keywords(&[Keyword::FROM,
Keyword::IN]) {
+ Some(Keyword::FROM) => ShowStatementInClause::FROM,
+ Some(Keyword::IN) => ShowStatementInClause::IN,
+ _ => return Ok(None),
+ };
+
+ if self.parse_keyword(Keyword::DATABASE) {
+ if self.peek_keywords(&[Keyword::STARTS, Keyword::WITH]) {
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Database),
+ parent_name: None,
+ }))
+ } else {
+ let parent_name = match self.parse_object_name(false) {
+ Ok(n) => Some(n),
+ Err(_) => None,
+ };
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Database),
+ parent_name,
+ }))
+ }
+ } else if self.parse_keyword(Keyword::SCHEMA) {
+ if self.peek_keywords(&[Keyword::STARTS, Keyword::WITH]) {
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Schema),
+ parent_name: None,
+ }))
+ } else {
+ let parent_name = match self.parse_object_name(false) {
+ Ok(n) => Some(n),
+ Err(_) => None,
+ };
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Schema),
+ parent_name,
+ }))
+ }
+ } else if self.parse_keyword(Keyword::ACCOUNT) {
+ let parent_name = match self.parse_object_name(false) {
+ Ok(n) => Some(n),
+ Err(_) => None,
+ };
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Account),
+ parent_name,
+ }))
+ } else if self.parse_keyword(Keyword::TABLE) {
+ let parent_name = match self.parse_object_name(false) {
+ Ok(n) => Some(n),
+ Err(_) => None,
+ };
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Table),
+ parent_name,
+ }))
+ } else if self.parse_keyword(Keyword::VIEW) {
+ let parent_name = match self.parse_object_name(false) {
+ Ok(n) => Some(n),
+ Err(_) => None,
+ };
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::View),
+ parent_name,
+ }))
+ } else {
+ // Parsing MySQL style FROM tbl_name FROM db_name
+ // which is equivalent to FROM tbl_name.db_name
+ let mut parent_name = self.parse_object_name(false)?;
+ if self
+ .parse_one_of_keywords(&[Keyword::FROM, Keyword::IN])
+ .is_some()
+ {
+ parent_name.0.insert(0, self.parse_identifier(false)?);
+ }
+
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: None,
+ parent_name: Some(parent_name),
+ }))
+ }
+ }
+
+ fn parse_show_opt_starts_with(&mut self) -> Result<Option<Value>,
ParserError> {
Review Comment:
```suggestion
fn maybe_parse_show_starts_with(&mut self) -> Result<Option<Value>,
ParserError> {
```
thinking similar here and for the others, the 'opt' in between reads
somewhat like its part of the type being parsed, might be easily recognizable
with the maybe prefix
##########
src/parser/mod.rs:
##########
@@ -12289,6 +12352,137 @@ impl<'a> Parser<'a> {
}
false
}
+
+ /// Look for an expected keyword, without consuming it
+ fn peek_keyword(&self, expected: Keyword) -> bool {
+ match self.peek_token().token {
+ Token::Word(w) => expected == w.keyword,
+ _ => false,
+ }
+ }
+
+ /// Look for one of expected keyword, without consuming it
+ fn peek_keywords(&self, expected: &[Keyword]) -> bool {
+ for kw in expected {
+ if self.peek_keyword(*kw) {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn parse_show_opt_in(&mut self) -> Result<Option<ShowStatementIn>,
ParserError> {
+ let clause = match self.parse_one_of_keywords(&[Keyword::FROM,
Keyword::IN]) {
+ Some(Keyword::FROM) => ShowStatementInClause::FROM,
+ Some(Keyword::IN) => ShowStatementInClause::IN,
+ _ => return Ok(None),
+ };
+
+ if self.parse_keyword(Keyword::DATABASE) {
Review Comment:
the branches look identical, can we do the match once so that we reuse the
same lookahead logic for all branches? thinking e.g roughly
```rust
let (parent_type, parent_name) = match
self.parse_one_of_keywords(&[Keyword::DATABASE,])? {
Some(Keyword::DATABASE) =>
(Some(ShowStatementInParentType::Database), None)
// ...
None => { /* mysql */ }
};
if parent_type.is_some() && self.peek_keywords(&[Keyword::STARTS,
Keyword::WITH]) {
Ok(Some(ShowStatementIn {
clause,
parent_type,
parent_name,
}))
} else {
let parent_name = match self.parse_object_name(false) {
Ok(n) => Some(n),
Err(_) => None,
};
Ok(Some(ShowStatementIn {
clause,
parent_type: Some(ShowStatementInParentType::Database),
parent_name,
}))
}
```
##########
src/parser/mod.rs:
##########
@@ -12289,6 +12352,137 @@ impl<'a> Parser<'a> {
}
false
}
+
+ /// Look for an expected keyword, without consuming it
+ fn peek_keyword(&self, expected: Keyword) -> bool {
+ match self.peek_token().token {
+ Token::Word(w) => expected == w.keyword,
+ _ => false,
+ }
+ }
+
+ /// Look for one of expected keyword, without consuming it
+ fn peek_keywords(&self, expected: &[Keyword]) -> bool {
+ for kw in expected {
+ if self.peek_keyword(*kw) {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn parse_show_opt_in(&mut self) -> Result<Option<ShowStatementIn>,
ParserError> {
+ let clause = match self.parse_one_of_keywords(&[Keyword::FROM,
Keyword::IN]) {
+ Some(Keyword::FROM) => ShowStatementInClause::FROM,
+ Some(Keyword::IN) => ShowStatementInClause::IN,
+ _ => return Ok(None),
+ };
+
+ if self.parse_keyword(Keyword::DATABASE) {
+ if self.peek_keywords(&[Keyword::STARTS, Keyword::WITH]) {
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Database),
+ parent_name: None,
+ }))
+ } else {
+ let parent_name = match self.parse_object_name(false) {
+ Ok(n) => Some(n),
+ Err(_) => None,
+ };
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Database),
+ parent_name,
+ }))
+ }
+ } else if self.parse_keyword(Keyword::SCHEMA) {
+ if self.peek_keywords(&[Keyword::STARTS, Keyword::WITH]) {
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Schema),
+ parent_name: None,
+ }))
+ } else {
+ let parent_name = match self.parse_object_name(false) {
+ Ok(n) => Some(n),
+ Err(_) => None,
+ };
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Schema),
+ parent_name,
+ }))
+ }
+ } else if self.parse_keyword(Keyword::ACCOUNT) {
+ let parent_name = match self.parse_object_name(false) {
+ Ok(n) => Some(n),
+ Err(_) => None,
+ };
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Account),
+ parent_name,
+ }))
+ } else if self.parse_keyword(Keyword::TABLE) {
+ let parent_name = match self.parse_object_name(false) {
+ Ok(n) => Some(n),
+ Err(_) => None,
+ };
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::Table),
+ parent_name,
+ }))
+ } else if self.parse_keyword(Keyword::VIEW) {
+ let parent_name = match self.parse_object_name(false) {
+ Ok(n) => Some(n),
+ Err(_) => None,
+ };
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: Some(ShowStatementInParentType::View),
+ parent_name,
+ }))
+ } else {
+ // Parsing MySQL style FROM tbl_name FROM db_name
+ // which is equivalent to FROM tbl_name.db_name
+ let mut parent_name = self.parse_object_name(false)?;
+ if self
+ .parse_one_of_keywords(&[Keyword::FROM, Keyword::IN])
+ .is_some()
+ {
+ parent_name.0.insert(0, self.parse_identifier(false)?);
+ }
+
+ Ok(Some(ShowStatementIn {
+ clause,
+ parent_type: None,
+ parent_name: Some(parent_name),
+ }))
+ }
+ }
+
+ fn parse_show_opt_starts_with(&mut self) -> Result<Option<Value>,
ParserError> {
+ match self.parse_keywords(&[Keyword::STARTS, Keyword::WITH]) {
+ true => Ok(Some(self.parse_value()?)),
+ false => Ok(None),
+ }
Review Comment:
nit: for these we could do an if/else instead of a match on a bool?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]