Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ execution consumer to validate. Never evaluate properties, normalize account ide
consume statement delimiters inside option loops, or infer runtime support from an AST.
Run the required fork checks below before committing changes to this syntax.

Namespace DESCRIBE backports retain this fork's existing VIEW representation
(`ExplainTable::has_view_keyword`), stage/file-format/sequence syntax and statement
boundaries. `DescribeObject` admits DATABASE/SCHEMA only; account namespaces must
not acquire table-relation visitor annotations. Parser tests do not establish
DataFusion or Rustice runtime support for the additive AST variant.

Snowflake CREATE TABLE's dedicated COMMENT option admits explicit `= NULL` as
`SqlOption::Comment(CommentDef::NullWithEq)` in plain table options. Keep existing
string variants and the shared `parse_comment_value` strict: column, stage, file
Expand Down
4 changes: 2 additions & 2 deletions examples/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ $ cargo run --example cli - [--dialectname]
.expect("failed to read from stdin");
String::from_utf8(buf).expect("stdin content wasn't valid utf8")
} else {
println!("Parsing from file '{}' using {:?}", &filename, dialect);
println!("Parsing from file '{}' using {:?}", filename, dialect);
fs::read_to_string(&filename)
.unwrap_or_else(|_| panic!("Unable to read the file {}", &filename))
.unwrap_or_else(|_| panic!("Unable to read the file {}", filename))
};
let without_bom = if contents.chars().next().unwrap() as u64 != 0xfeff {
contents.as_str()
Expand Down
2 changes: 1 addition & 1 deletion src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4704,7 +4704,7 @@ impl fmt::Display for AlterTable {
if self.only {
write!(f, "ONLY ")?;
}
write!(f, "{} ", &self.name)?;
write!(f, "{} ", self.name)?;
if let Some(cluster) = &self.on_cluster {
write!(f, "ON CLUSTER {cluster} ")?;
}
Expand Down
42 changes: 41 additions & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4745,6 +4745,12 @@ pub enum Statement {
name: ObjectName,
},
/// ```sql
/// DESC | DESCRIBE { DATABASE | SCHEMA } <name>
/// ```
/// See [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] <statement>
/// ```
Explain {
Expand Down Expand Up @@ -5252,6 +5258,7 @@ impl fmt::Display for Statement {

write!(f, "{id}")
}
Statement::DescribeObject(s) => write!(f, "{s}"),
Statement::ExplainTable {
describe_alias,
hive_format,
Expand Down Expand Up @@ -9086,6 +9093,33 @@ impl fmt::Display for DescribeAlias {
}
}

/// ```sql
/// DESC | DESCRIBE { DATABASE | SCHEMA } <name>
/// ```
/// See [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: `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))]
Expand Down Expand Up @@ -12174,7 +12208,7 @@ impl fmt::Display for AlterUser {
let has_props = !self.set_props.options.is_empty();
if has_props {
write!(f, " SET")?;
write!(f, " {}", &self.set_props)?;
write!(f, " {}", self.set_props)?;
}
if !self.unset_props.is_empty() {
write!(f, " UNSET {}", display_comma_separated(&self.unset_props))?;
Expand Down Expand Up @@ -12874,6 +12908,12 @@ impl From<CreateUser> for Statement {
}
}

impl From<DescribeObject> for Statement {
fn from(d: DescribeObject) -> Self {
Self::DescribeObject(d)
}
}

impl From<VacuumStatement> for Statement {
fn from(v: VacuumStatement) -> Self {
Self::Vacuum(v)
Expand Down
2 changes: 1 addition & 1 deletion src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3554,7 +3554,7 @@ pub struct LockClause {

impl fmt::Display for LockClause {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "FOR {}", &self.lock_type)?;
write!(f, "FOR {}", self.lock_type)?;
if let Some(ref of) = self.of {
write!(f, " OF {of}")?;
}
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ impl Spanned for Statement {
Statement::DescribeStage { .. } => Span::empty(),
Statement::DescribeFileFormat { .. } => Span::empty(),
Statement::DescribeSequence { .. } => Span::empty(),
Statement::DescribeObject(..) => Span::empty(),
Statement::Explain { .. } => Span::empty(),
Statement::Savepoint { .. } => Span::empty(),
Statement::ReleaseSavepoint { .. } => Span::empty(),
Expand Down
3 changes: 3 additions & 0 deletions src/dialect/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,4 +312,7 @@ impl Dialect for GenericDialect {
fn supports_xml_expressions(&self) -> bool {
true
}
fn supports_describe_object(&self) -> bool {
true
}
}
9 changes: 9 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,15 @@ pub trait Dialect: Debug + Any {
false
}

/// Returns true if this dialect supports `DESC | DESCRIBE <object_type> <name>`
/// for namespace object types: `DESCRIBE DATABASE my_db` or `DESCRIBE SCHEMA my_schema`.
/// Existing `DESCRIBE VIEW` support keeps its `ExplainTable` representation.
///
/// See <https://docs.snowflake.com/en/sql-reference/sql/desc>
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
Expand Down
4 changes: 4 additions & 0 deletions src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,10 @@ impl Dialect for SnowflakeDialect {
true
}

fn supports_describe_object(&self) -> bool {
true
}

fn allow_extract_custom(&self) -> bool {
true
}
Expand Down
36 changes: 32 additions & 4 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4761,7 +4761,7 @@ impl<'a> Parser<'a> {
if self.parse_keyword(expected) {
Ok(self.get_current_token().clone())
} else {
self.expected_ref(format!("{:?}", &expected).as_str(), self.peek_token_ref())
self.expected_ref(format!("{:?}", expected).as_str(), self.peek_token_ref())
}
}

Expand All @@ -4774,7 +4774,7 @@ impl<'a> Parser<'a> {
if self.parse_keyword(expected) {
Ok(())
} else {
self.expected_ref(format!("{:?}", &expected).as_str(), self.peek_token_ref())
self.expected_ref(format!("{:?}", expected).as_str(), self.peek_token_ref())
}
}

Expand Down Expand Up @@ -13436,7 +13436,7 @@ impl<'a> Parser<'a> {
token => {
return Err(ParserError::ParserError(format!(
"Unexpected token in identifier: {token}"
)))?;
)));
}
}
}
Expand Down Expand Up @@ -14070,6 +14070,11 @@ impl<'a> Parser<'a> {
&mut self,
describe_alias: DescribeAlias,
) -> Result<Statement, ParserError> {
if describe_alias != DescribeAlias::Explain && self.dialect.supports_describe_object() {
if let Some(statement) = self.parse_describe_object(describe_alias)? {
return Ok(statement);
}
}
let mut analyze = false;
let mut verbose = false;
let mut query_plan = false;
Expand Down Expand Up @@ -14170,6 +14175,29 @@ impl<'a> Parser<'a> {
}
}

/// Parse `{ DATABASE | SCHEMA } <name>` following `DESC | DESCRIBE`.
/// VIEW retains this fork's existing `ExplainTable::has_view_keyword` representation.
/// Returns `None` if none of the object type keywords is present.
fn parse_describe_object(
&mut self,
describe_alias: DescribeAlias,
) -> Result<Option<Statement>, ParserError> {
let object_type = match self.parse_one_of_keywords(&[Keyword::DATABASE, Keyword::SCHEMA]) {
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
Expand Down Expand Up @@ -16869,7 +16897,7 @@ impl<'a> Parser<'a> {
tok.token
),
tok.span.start
)?;
);
}
}

Expand Down
Loading
Loading