Skip to content

Commit 76322ba

Browse files
authored
Add support for Snowflake SHOW DATABASES/SCHEMAS/TABLES/VIEWS/COLUMNS statements (#1501)
1 parent 632ba4c commit 76322ba

File tree

8 files changed

+591
-159
lines changed

8 files changed

+591
-159
lines changed

src/ast/mod.rs

+161-62
Original file line numberDiff line numberDiff line change
@@ -2773,41 +2773,45 @@ pub enum Statement {
27732773
/// ```sql
27742774
/// SHOW COLUMNS
27752775
/// ```
2776-
///
2777-
/// Note: this is a MySQL-specific statement.
27782776
ShowColumns {
27792777
extended: bool,
27802778
full: bool,
2781-
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
2782-
table_name: ObjectName,
2783-
filter: Option<ShowStatementFilter>,
2779+
show_options: ShowStatementOptions,
27842780
},
27852781
/// ```sql
2786-
/// SHOW DATABASES [LIKE 'pattern']
2782+
/// SHOW DATABASES
27872783
/// ```
2788-
ShowDatabases { filter: Option<ShowStatementFilter> },
2784+
ShowDatabases {
2785+
terse: bool,
2786+
history: bool,
2787+
show_options: ShowStatementOptions,
2788+
},
27892789
/// ```sql
2790-
/// SHOW SCHEMAS [LIKE 'pattern']
2790+
/// SHOW SCHEMAS
27912791
/// ```
2792-
ShowSchemas { filter: Option<ShowStatementFilter> },
2792+
ShowSchemas {
2793+
terse: bool,
2794+
history: bool,
2795+
show_options: ShowStatementOptions,
2796+
},
27932797
/// ```sql
27942798
/// SHOW TABLES
27952799
/// ```
27962800
ShowTables {
2801+
terse: bool,
2802+
history: bool,
27972803
extended: bool,
27982804
full: bool,
2799-
clause: Option<ShowClause>,
2800-
db_name: Option<Ident>,
2801-
filter: Option<ShowStatementFilter>,
2805+
external: bool,
2806+
show_options: ShowStatementOptions,
28022807
},
28032808
/// ```sql
28042809
/// SHOW VIEWS
28052810
/// ```
28062811
ShowViews {
2812+
terse: bool,
28072813
materialized: bool,
2808-
clause: Option<ShowClause>,
2809-
db_name: Option<Ident>,
2810-
filter: Option<ShowStatementFilter>,
2814+
show_options: ShowStatementOptions,
28112815
},
28122816
/// ```sql
28132817
/// SHOW COLLATION
@@ -4387,79 +4391,72 @@ impl fmt::Display for Statement {
43874391
Statement::ShowColumns {
43884392
extended,
43894393
full,
4390-
table_name,
4391-
filter,
4394+
show_options,
43924395
} => {
43934396
write!(
43944397
f,
4395-
"SHOW {extended}{full}COLUMNS FROM {table_name}",
4398+
"SHOW {extended}{full}COLUMNS{show_options}",
43964399
extended = if *extended { "EXTENDED " } else { "" },
43974400
full = if *full { "FULL " } else { "" },
4398-
table_name = table_name,
43994401
)?;
4400-
if let Some(filter) = filter {
4401-
write!(f, " {filter}")?;
4402-
}
44034402
Ok(())
44044403
}
4405-
Statement::ShowDatabases { filter } => {
4406-
write!(f, "SHOW DATABASES")?;
4407-
if let Some(filter) = filter {
4408-
write!(f, " {filter}")?;
4409-
}
4404+
Statement::ShowDatabases {
4405+
terse,
4406+
history,
4407+
show_options,
4408+
} => {
4409+
write!(
4410+
f,
4411+
"SHOW {terse}DATABASES{history}{show_options}",
4412+
terse = if *terse { "TERSE " } else { "" },
4413+
history = if *history { " HISTORY" } else { "" },
4414+
)?;
44104415
Ok(())
44114416
}
4412-
Statement::ShowSchemas { filter } => {
4413-
write!(f, "SHOW SCHEMAS")?;
4414-
if let Some(filter) = filter {
4415-
write!(f, " {filter}")?;
4416-
}
4417+
Statement::ShowSchemas {
4418+
terse,
4419+
history,
4420+
show_options,
4421+
} => {
4422+
write!(
4423+
f,
4424+
"SHOW {terse}SCHEMAS{history}{show_options}",
4425+
terse = if *terse { "TERSE " } else { "" },
4426+
history = if *history { " HISTORY" } else { "" },
4427+
)?;
44174428
Ok(())
44184429
}
44194430
Statement::ShowTables {
4431+
terse,
4432+
history,
44204433
extended,
44214434
full,
4422-
clause: show_clause,
4423-
db_name,
4424-
filter,
4435+
external,
4436+
show_options,
44254437
} => {
44264438
write!(
44274439
f,
4428-
"SHOW {extended}{full}TABLES",
4440+
"SHOW {terse}{extended}{full}{external}TABLES{history}{show_options}",
4441+
terse = if *terse { "TERSE " } else { "" },
44294442
extended = if *extended { "EXTENDED " } else { "" },
44304443
full = if *full { "FULL " } else { "" },
4444+
external = if *external { "EXTERNAL " } else { "" },
4445+
history = if *history { " HISTORY" } else { "" },
44314446
)?;
4432-
if let Some(show_clause) = show_clause {
4433-
write!(f, " {show_clause}")?;
4434-
}
4435-
if let Some(db_name) = db_name {
4436-
write!(f, " {db_name}")?;
4437-
}
4438-
if let Some(filter) = filter {
4439-
write!(f, " {filter}")?;
4440-
}
44414447
Ok(())
44424448
}
44434449
Statement::ShowViews {
4450+
terse,
44444451
materialized,
4445-
clause: show_clause,
4446-
db_name,
4447-
filter,
4452+
show_options,
44484453
} => {
44494454
write!(
44504455
f,
4451-
"SHOW {}VIEWS",
4452-
if *materialized { "MATERIALIZED " } else { "" }
4456+
"SHOW {terse}{materialized}VIEWS{show_options}",
4457+
terse = if *terse { "TERSE " } else { "" },
4458+
materialized = if *materialized { "MATERIALIZED " } else { "" }
44534459
)?;
4454-
if let Some(show_clause) = show_clause {
4455-
write!(f, " {show_clause}")?;
4456-
}
4457-
if let Some(db_name) = db_name {
4458-
write!(f, " {db_name}")?;
4459-
}
4460-
if let Some(filter) = filter {
4461-
write!(f, " {filter}")?;
4462-
}
44634460
Ok(())
44644461
}
44654462
Statement::ShowFunctions { filter } => {
@@ -6172,14 +6169,14 @@ impl fmt::Display for ShowStatementFilter {
61726169
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
61736170
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
61746171
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
6175-
pub enum ShowClause {
6172+
pub enum ShowStatementInClause {
61766173
IN,
61776174
FROM,
61786175
}
61796176

6180-
impl fmt::Display for ShowClause {
6177+
impl fmt::Display for ShowStatementInClause {
61816178
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6182-
use ShowClause::*;
6179+
use ShowStatementInClause::*;
61836180
match self {
61846181
FROM => write!(f, "FROM"),
61856182
IN => write!(f, "IN"),
@@ -7357,6 +7354,108 @@ impl Display for UtilityOption {
73577354
}
73587355
}
73597356

7357+
/// Represents the different options available for `SHOW`
7358+
/// statements to filter the results. Example from Snowflake:
7359+
/// <https://docs.snowflake.com/en/sql-reference/sql/show-tables>
7360+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7361+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7362+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7363+
pub struct ShowStatementOptions {
7364+
pub show_in: Option<ShowStatementIn>,
7365+
pub starts_with: Option<Value>,
7366+
pub limit: Option<Expr>,
7367+
pub limit_from: Option<Value>,
7368+
pub filter_position: Option<ShowStatementFilterPosition>,
7369+
}
7370+
7371+
impl Display for ShowStatementOptions {
7372+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7373+
let (like_in_infix, like_in_suffix) = match &self.filter_position {
7374+
Some(ShowStatementFilterPosition::Infix(filter)) => {
7375+
(format!(" {filter}"), "".to_string())
7376+
}
7377+
Some(ShowStatementFilterPosition::Suffix(filter)) => {
7378+
("".to_string(), format!(" {filter}"))
7379+
}
7380+
None => ("".to_string(), "".to_string()),
7381+
};
7382+
write!(
7383+
f,
7384+
"{like_in_infix}{show_in}{starts_with}{limit}{from}{like_in_suffix}",
7385+
show_in = match &self.show_in {
7386+
Some(i) => format!(" {i}"),
7387+
None => String::new(),
7388+
},
7389+
starts_with = match &self.starts_with {
7390+
Some(s) => format!(" STARTS WITH {s}"),
7391+
None => String::new(),
7392+
},
7393+
limit = match &self.limit {
7394+
Some(l) => format!(" LIMIT {l}"),
7395+
None => String::new(),
7396+
},
7397+
from = match &self.limit_from {
7398+
Some(f) => format!(" FROM {f}"),
7399+
None => String::new(),
7400+
}
7401+
)?;
7402+
Ok(())
7403+
}
7404+
}
7405+
7406+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7407+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7408+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7409+
pub enum ShowStatementFilterPosition {
7410+
Infix(ShowStatementFilter), // For example: SHOW COLUMNS LIKE '%name%' IN TABLE tbl
7411+
Suffix(ShowStatementFilter), // For example: SHOW COLUMNS IN tbl LIKE '%name%'
7412+
}
7413+
7414+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7415+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7416+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7417+
pub enum ShowStatementInParentType {
7418+
Account,
7419+
Database,
7420+
Schema,
7421+
Table,
7422+
View,
7423+
}
7424+
7425+
impl fmt::Display for ShowStatementInParentType {
7426+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7427+
match self {
7428+
ShowStatementInParentType::Account => write!(f, "ACCOUNT"),
7429+
ShowStatementInParentType::Database => write!(f, "DATABASE"),
7430+
ShowStatementInParentType::Schema => write!(f, "SCHEMA"),
7431+
ShowStatementInParentType::Table => write!(f, "TABLE"),
7432+
ShowStatementInParentType::View => write!(f, "VIEW"),
7433+
}
7434+
}
7435+
}
7436+
7437+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7438+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7439+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7440+
pub struct ShowStatementIn {
7441+
pub clause: ShowStatementInClause,
7442+
pub parent_type: Option<ShowStatementInParentType>,
7443+
pub parent_name: Option<ObjectName>,
7444+
}
7445+
7446+
impl fmt::Display for ShowStatementIn {
7447+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7448+
write!(f, "{}", self.clause)?;
7449+
if let Some(parent_type) = &self.parent_type {
7450+
write!(f, " {}", parent_type)?;
7451+
}
7452+
if let Some(parent_name) = &self.parent_name {
7453+
write!(f, " {}", parent_name)?;
7454+
}
7455+
Ok(())
7456+
}
7457+
}
7458+
73607459
#[cfg(test)]
73617460
mod tests {
73627461
use super::*;

src/dialect/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,12 @@ pub trait Dialect: Debug + Any {
622622
fn supports_boolean_literals(&self) -> bool {
623623
true
624624
}
625+
626+
/// Returns true if this dialect supports the `LIKE 'pattern'` option in
627+
/// a `SHOW` statement before the `IN` option
628+
fn supports_show_like_before_in(&self) -> bool {
629+
false
630+
}
625631
}
626632

627633
/// This represents the operators for which precedence must be defined

src/dialect/snowflake.rs

+6
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ impl Dialect for SnowflakeDialect {
203203
fn allow_extract_single_quotes(&self) -> bool {
204204
true
205205
}
206+
207+
/// Snowflake expects the `LIKE` option before the `IN` option,
208+
/// for example: <https://docs.snowflake.com/en/sql-reference/sql/show-views#syntax>
209+
fn supports_show_like_before_in(&self) -> bool {
210+
true
211+
}
206212
}
207213

208214
/// Parse snowflake create table statement.

src/keywords.rs

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ define_keywords!(
7676
ABS,
7777
ABSOLUTE,
7878
ACCESS,
79+
ACCOUNT,
7980
ACTION,
8081
ADD,
8182
ADMIN,
@@ -91,6 +92,7 @@ define_keywords!(
9192
AND,
9293
ANTI,
9394
ANY,
95+
APPLICATION,
9496
APPLY,
9597
ARCHIVE,
9698
ARE,
@@ -710,6 +712,7 @@ define_keywords!(
710712
STABLE,
711713
STAGE,
712714
START,
715+
STARTS,
713716
STATEMENT,
714717
STATIC,
715718
STATISTICS,
@@ -746,6 +749,7 @@ define_keywords!(
746749
TEMP,
747750
TEMPORARY,
748751
TERMINATED,
752+
TERSE,
749753
TEXT,
750754
TEXTFILE,
751755
THEN,

0 commit comments

Comments
 (0)