Skip to content

Commit

Permalink
Add support for Snowflake SHOW DATABASES/SCHEMAS/TABLES/VIEWS/COLUMNS…
Browse files Browse the repository at this point in the history
… statements (#1501)
  • Loading branch information
yoavcloud authored Nov 13, 2024
1 parent 632ba4c commit 76322ba
Show file tree
Hide file tree
Showing 8 changed files with 591 additions and 159 deletions.
223 changes: 161 additions & 62 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2773,41 +2773,45 @@ 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,
filter: Option<ShowStatementFilter>,
show_options: ShowStatementOptions,
},
/// ```sql
/// SHOW DATABASES [LIKE 'pattern']
/// SHOW DATABASES
/// ```
ShowDatabases { filter: Option<ShowStatementFilter> },
ShowDatabases {
terse: bool,
history: bool,
show_options: ShowStatementOptions,
},
/// ```sql
/// SHOW SCHEMAS [LIKE 'pattern']
/// SHOW SCHEMAS
/// ```
ShowSchemas { filter: Option<ShowStatementFilter> },
ShowSchemas {
terse: bool,
history: bool,
show_options: ShowStatementOptions,
},
/// ```sql
/// SHOW TABLES
/// ```
ShowTables {
terse: bool,
history: bool,
extended: bool,
full: bool,
clause: Option<ShowClause>,
db_name: Option<Ident>,
filter: Option<ShowStatementFilter>,
external: bool,
show_options: ShowStatementOptions,
},
/// ```sql
/// SHOW VIEWS
/// ```
ShowViews {
terse: bool,
materialized: bool,
clause: Option<ShowClause>,
db_name: Option<Ident>,
filter: Option<ShowStatementFilter>,
show_options: ShowStatementOptions,
},
/// ```sql
/// SHOW COLLATION
Expand Down Expand Up @@ -4387,79 +4391,72 @@ impl fmt::Display for Statement {
Statement::ShowColumns {
extended,
full,
table_name,
filter,
show_options,
} => {
write!(
f,
"SHOW {extended}{full}COLUMNS FROM {table_name}",
"SHOW {extended}{full}COLUMNS{show_options}",
extended = if *extended { "EXTENDED " } else { "" },
full = if *full { "FULL " } else { "" },
table_name = table_name,
)?;
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,
show_options,
} => {
write!(
f,
"SHOW {terse}DATABASES{history}{show_options}",
terse = if *terse { "TERSE " } else { "" },
history = if *history { " HISTORY" } else { "" },
)?;
Ok(())
}
Statement::ShowSchemas { filter } => {
write!(f, "SHOW SCHEMAS")?;
if let Some(filter) = filter {
write!(f, " {filter}")?;
}
Statement::ShowSchemas {
terse,
history,
show_options,
} => {
write!(
f,
"SHOW {terse}SCHEMAS{history}{show_options}",
terse = if *terse { "TERSE " } else { "" },
history = if *history { " HISTORY" } else { "" },
)?;
Ok(())
}
Statement::ShowTables {
terse,
history,
extended,
full,
clause: show_clause,
db_name,
filter,
external,
show_options,
} => {
write!(
f,
"SHOW {extended}{full}TABLES",
"SHOW {terse}{extended}{full}{external}TABLES{history}{show_options}",
terse = if *terse { "TERSE " } else { "" },
extended = if *extended { "EXTENDED " } else { "" },
full = if *full { "FULL " } else { "" },
external = if *external { "EXTERNAL " } else { "" },
history = if *history { " HISTORY" } 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(())
}
Statement::ShowViews {
terse,
materialized,
clause: show_clause,
db_name,
filter,
show_options,
} => {
write!(
f,
"SHOW {}VIEWS",
if *materialized { "MATERIALIZED " } else { "" }
"SHOW {terse}{materialized}VIEWS{show_options}",
terse = if *terse { "TERSE " } else { "" },
materialized = if *materialized { "MATERIALIZED " } 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(())
}
Statement::ShowFunctions { filter } => {
Expand Down Expand Up @@ -6172,14 +6169,14 @@ impl fmt::Display for ShowStatementFilter {
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ShowClause {
pub enum ShowStatementInClause {
IN,
FROM,
}

impl fmt::Display for ShowClause {
impl fmt::Display for ShowStatementInClause {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ShowClause::*;
use ShowStatementInClause::*;
match self {
FROM => write!(f, "FROM"),
IN => write!(f, "IN"),
Expand Down Expand Up @@ -7357,6 +7354,108 @@ impl Display for UtilityOption {
}
}

/// Represents the different options available for `SHOW`
/// statements to filter the results. Example from Snowflake:
/// <https://docs.snowflake.com/en/sql-reference/sql/show-tables>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ShowStatementOptions {
pub show_in: Option<ShowStatementIn>,
pub starts_with: Option<Value>,
pub limit: Option<Expr>,
pub limit_from: Option<Value>,
pub filter_position: Option<ShowStatementFilterPosition>,
}

impl Display for ShowStatementOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (like_in_infix, like_in_suffix) = match &self.filter_position {
Some(ShowStatementFilterPosition::Infix(filter)) => {
(format!(" {filter}"), "".to_string())
}
Some(ShowStatementFilterPosition::Suffix(filter)) => {
("".to_string(), format!(" {filter}"))
}
None => ("".to_string(), "".to_string()),
};
write!(
f,
"{like_in_infix}{show_in}{starts_with}{limit}{from}{like_in_suffix}",
show_in = match &self.show_in {
Some(i) => format!(" {i}"),
None => String::new(),
},
starts_with = match &self.starts_with {
Some(s) => format!(" STARTS WITH {s}"),
None => String::new(),
},
limit = match &self.limit {
Some(l) => format!(" LIMIT {l}"),
None => String::new(),
},
from = match &self.limit_from {
Some(f) => format!(" FROM {f}"),
None => String::new(),
}
)?;
Ok(())
}
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ShowStatementFilterPosition {
Infix(ShowStatementFilter), // For example: SHOW COLUMNS LIKE '%name%' IN TABLE tbl
Suffix(ShowStatementFilter), // For example: SHOW COLUMNS IN tbl LIKE '%name%'
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ShowStatementInParentType {
Account,
Database,
Schema,
Table,
View,
}

impl fmt::Display for ShowStatementInParentType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ShowStatementInParentType::Account => write!(f, "ACCOUNT"),
ShowStatementInParentType::Database => write!(f, "DATABASE"),
ShowStatementInParentType::Schema => write!(f, "SCHEMA"),
ShowStatementInParentType::Table => write!(f, "TABLE"),
ShowStatementInParentType::View => write!(f, "VIEW"),
}
}
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ShowStatementIn {
pub clause: ShowStatementInClause,
pub parent_type: Option<ShowStatementInParentType>,
pub parent_name: Option<ObjectName>,
}

impl fmt::Display for ShowStatementIn {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.clause)?;
if let Some(parent_type) = &self.parent_type {
write!(f, " {}", parent_type)?;
}
if let Some(parent_name) = &self.parent_name {
write!(f, " {}", parent_name)?;
}
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
6 changes: 6 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,12 @@ pub trait Dialect: Debug + Any {
fn supports_boolean_literals(&self) -> bool {
true
}

/// Returns true if this dialect supports the `LIKE 'pattern'` option in
/// a `SHOW` statement before the `IN` option
fn supports_show_like_before_in(&self) -> bool {
false
}
}

/// This represents the operators for which precedence must be defined
Expand Down
6 changes: 6 additions & 0 deletions src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@ impl Dialect for SnowflakeDialect {
fn allow_extract_single_quotes(&self) -> bool {
true
}

/// Snowflake expects the `LIKE` option before the `IN` option,
/// for example: <https://docs.snowflake.com/en/sql-reference/sql/show-views#syntax>
fn supports_show_like_before_in(&self) -> bool {
true
}
}

/// Parse snowflake create table statement.
Expand Down
4 changes: 4 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ define_keywords!(
ABS,
ABSOLUTE,
ACCESS,
ACCOUNT,
ACTION,
ADD,
ADMIN,
Expand All @@ -91,6 +92,7 @@ define_keywords!(
AND,
ANTI,
ANY,
APPLICATION,
APPLY,
ARCHIVE,
ARE,
Expand Down Expand Up @@ -710,6 +712,7 @@ define_keywords!(
STABLE,
STAGE,
START,
STARTS,
STATEMENT,
STATIC,
STATISTICS,
Expand Down Expand Up @@ -746,6 +749,7 @@ define_keywords!(
TEMP,
TEMPORARY,
TERMINATED,
TERSE,
TEXT,
TEXTFILE,
THEN,
Expand Down
Loading

0 comments on commit 76322ba

Please sign in to comment.