From e7278099a374e7077d10817a291a3d7e14fa87e4 Mon Sep 17 00:00:00 2001 From: sabir-akhadov-localstack Date: Thu, 29 Oct 2026 15:08:35 +0100 Subject: [PATCH] Snowflake: parse DESC[RIBE] { VIEW | DATABASE | SCHEMA } Co-Authored-By: Claude Opus 5.5 --- src/ast/mod.rs | 42 +++++++++++++++++++++++++++++++++++++++ src/ast/spans.rs | 1 + src/dialect/generic.rs | 4 ++++ src/dialect/mod.rs | 9 +++++++++ src/dialect/snowflake.rs | 4 ++++ src/parser/mod.rs | 35 ++++++++++++++++++++++++++++++++ tests/sqlparser_common.rs | 15 ++++++++++++++ 7 files changed, 110 insertions(+) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 0fa3841fd5..a71b8fa193 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4772,6 +4772,13 @@ pub enum Statement { table_name: ObjectName, }, /// ```sql + /// DESC | DESCRIBE { VIEW | DATABASE | SCHEMA } + /// ``` + /// See [DESCRIBE VIEW](https://docs.snowflake.com/en/sql-reference/sql/desc-view), + /// [DESCRIBE DATABASE](https://docs.snowflake.com/en/sql-reference/sql/desc-database) + /// and [DESCRIBE SCHEMA](https://docs.snowflake.com/en/sql-reference/sql/desc-schema) + DescribeObject(DescribeObject), + /// ```sql /// [EXPLAIN | DESC | DESCRIBE] /// ``` Explain { @@ -5259,6 +5266,7 @@ impl fmt::Display for Statement { write!(f, "{id}") } + Statement::DescribeObject(s) => write!(f, "{s}"), Statement::ExplainTable { describe_alias, hive_format, @@ -8986,6 +8994,34 @@ impl fmt::Display for DescribeAlias { } } +/// ```sql +/// DESC | DESCRIBE { VIEW | DATABASE | SCHEMA } +/// ``` +/// See [DESCRIBE VIEW](https://docs.snowflake.com/en/sql-reference/sql/desc-view), +/// [DESCRIBE DATABASE](https://docs.snowflake.com/en/sql-reference/sql/desc-database) +/// and [DESCRIBE SCHEMA](https://docs.snowflake.com/en/sql-reference/sql/desc-schema) +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct DescribeObject { + /// The keyword used, `DESC` or `DESCRIBE`. + pub describe_alias: DescribeAlias, + /// The kind of object described: `VIEW`, `DATABASE` or `SCHEMA`. + pub object_type: ObjectType, + /// Name of the object. + pub name: ObjectName, +} + +impl fmt::Display for DescribeObject { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} {} {}", + self.describe_alias, self.object_type, self.name + ) + } +} + #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] @@ -12751,6 +12787,12 @@ impl From for Statement { } } +impl From for Statement { + fn from(d: DescribeObject) -> Self { + Self::DescribeObject(d) + } +} + impl From for Statement { fn from(v: VacuumStatement) -> Self { Self::Vacuum(v) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 704b583b8b..df8b2db889 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -478,6 +478,7 @@ impl Spanned for Statement { Statement::Prepare { .. } => Span::empty(), Statement::Kill { .. } => Span::empty(), Statement::ExplainTable { .. } => Span::empty(), + Statement::DescribeObject(..) => Span::empty(), Statement::Explain { .. } => Span::empty(), Statement::Savepoint { .. } => Span::empty(), Statement::ReleaseSavepoint { .. } => Span::empty(), diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs index 6745bf8d11..049cf2c542 100644 --- a/src/dialect/generic.rs +++ b/src/dialect/generic.rs @@ -372,4 +372,8 @@ impl Dialect for GenericDialect { fn supports_add_column_if_not_exists(&self) -> bool { true } + + fn supports_describe_object(&self) -> bool { + true + } } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 8d92f0b57d..857512147e 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1192,6 +1192,15 @@ pub trait Dialect: Debug + Any { false } + /// Returns true if this dialect supports `DESC | DESCRIBE ` + /// for object types other than `TABLE`, e.g. `DESCRIBE VIEW my_view`, + /// `DESCRIBE DATABASE my_db` or `DESCRIBE SCHEMA my_schema`. + /// + /// See + fn supports_describe_object(&self) -> bool { + false + } + /// Returns true if this dialect allows the `EXTRACT` function to words other than [`Keyword`]. fn allow_extract_custom(&self) -> bool { false diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index dc462f5ba2..3b91ef3a7d 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -479,6 +479,10 @@ impl Dialect for SnowflakeDialect { true } + fn supports_describe_object(&self) -> bool { + true + } + fn allow_extract_custom(&self) -> bool { true } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 90296755ab..764f65f922 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -15190,6 +15190,14 @@ impl<'a> Parser<'a> { options, }), _ => { + if describe_alias != DescribeAlias::Explain + && self.dialect.supports_describe_object() + { + if let Some(statement) = self.parse_describe_object(describe_alias)? { + return Ok(statement); + } + } + let hive_format = match self.parse_one_of_keywords(&[Keyword::EXTENDED, Keyword::FORMATTED]) { Some(Keyword::EXTENDED) => Some(HiveDescribeFormat::Extended), @@ -15215,6 +15223,33 @@ impl<'a> Parser<'a> { } } + /// Parse `{ VIEW | DATABASE | SCHEMA } ` following `DESC | DESCRIBE`. + /// Returns `None` if none of the object type keywords is present. + fn parse_describe_object( + &mut self, + describe_alias: DescribeAlias, + ) -> Result, ParserError> { + let object_type = match self.parse_one_of_keywords(&[ + Keyword::VIEW, + Keyword::DATABASE, + Keyword::SCHEMA, + ]) { + Some(Keyword::VIEW) => ObjectType::View, + Some(Keyword::DATABASE) => ObjectType::Database, + Some(Keyword::SCHEMA) => ObjectType::Schema, + _ => return Ok(None), + }; + let name = self.parse_object_name(false)?; + Ok(Some( + DescribeObject { + describe_alias, + object_type, + name, + } + .into(), + )) + } + /// Parse a query expression, i.e. a `SELECT` statement optionally /// preceded with some `WITH` CTE declarations and optionally followed /// by `ORDER BY`. Unlike some other parse_... methods, this one doesn't diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 379e37a660..74905a82d7 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -22744,3 +22744,18 @@ fn display_create_procedure_parameter_list_propagates_write_errors() { let mut writer = RejectingWriter(" ("); assert_eq!(write!(writer, "{statement}"), Err(fmt::Error)); } + +#[test] +fn parse_describe_object() { + let dialects = all_dialects_where(|d| d.supports_describe_object()); + dialects.verified_stmt("DESCRIBE VIEW v"); + dialects.verified_stmt("DESC VIEW db.s.v"); + dialects.verified_stmt("DESCRIBE DATABASE d"); + dialects.verified_stmt("DESC SCHEMA db.s"); + + let dialects = all_dialects_where(|d| !d.supports_describe_object()); + assert!(matches!( + dialects.verified_stmt("DESC schema"), + Statement::ExplainTable { .. } + )); +}