Skip to content

Commit a6a00a8

Browse files
Snowflake: parse SHOW STREAMS / DESCRIBE STREAM / ALTER STREAM COMMENT
Adds ShowStreams and AlterStream statements, DescribeObjectType::Stream, the STREAMS keyword, and an EOF-anchored error for the invalid DESCRIBE TABLE STREAM <name> shape (matching real Snowflake).
1 parent cc25e59 commit a6a00a8

4 files changed

Lines changed: 118 additions & 1 deletion

File tree

src/ast/mod.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5011,6 +5011,29 @@ pub enum Statement {
50115011
show_options: ShowStatementOptions,
50125012
},
50135013
/// ```sql
5014+
/// SHOW [TERSE] STREAMS [LIKE '<pattern>']
5015+
/// [IN { ACCOUNT | DATABASE <db> | SCHEMA <schema> }]
5016+
/// ```
5017+
/// See <https://docs.snowflake.com/en/sql-reference/sql/show-streams>
5018+
ShowStreams {
5019+
/// `true` when terse output format was requested.
5020+
terse: bool,
5021+
/// Additional options for `SHOW` statements.
5022+
show_options: ShowStatementOptions,
5023+
},
5024+
/// ```sql
5025+
/// ALTER STREAM [IF EXISTS] <name> { SET COMMENT = '<string>' | UNSET COMMENT }
5026+
/// ```
5027+
/// See <https://docs.snowflake.com/en/sql-reference/sql/alter-stream>
5028+
AlterStream {
5029+
/// `IF EXISTS` flag.
5030+
if_exists: bool,
5031+
/// Stream name.
5032+
name: ObjectName,
5033+
/// The alter action.
5034+
operation: AlterStreamOperation,
5035+
},
5036+
/// ```sql
50145037
/// CREATE [OR REPLACE] EXTERNAL VOLUME [IF NOT EXISTS] <name>
50155038
/// ```
50165039
/// See <https://docs.snowflake.com/en/sql-reference/sql/create-external-volume>
@@ -7353,6 +7376,28 @@ impl fmt::Display for Statement {
73537376
)?;
73547377
Ok(())
73557378
}
7379+
Statement::ShowStreams {
7380+
terse,
7381+
show_options,
7382+
} => {
7383+
write!(
7384+
f,
7385+
"SHOW {terse}STREAMS{show_options}",
7386+
terse = if *terse { "TERSE " } else { "" },
7387+
)?;
7388+
Ok(())
7389+
}
7390+
Statement::AlterStream {
7391+
if_exists,
7392+
name,
7393+
operation,
7394+
} => {
7395+
write!(f, "ALTER STREAM")?;
7396+
if *if_exists {
7397+
write!(f, " IF EXISTS")?;
7398+
}
7399+
write!(f, " {name} {operation}")
7400+
}
73567401
Statement::CreateExternalVolume {
73577402
or_replace,
73587403
if_not_exists,
@@ -10337,6 +10382,8 @@ pub enum DescribeObjectType {
1033710382
Task,
1033810383
/// `STAGE`
1033910384
Stage,
10385+
/// `STREAM`
10386+
Stream,
1034010387
}
1034110388

1034210389
impl fmt::Display for DescribeObjectType {
@@ -10349,6 +10396,7 @@ impl fmt::Display for DescribeObjectType {
1034910396
DescribeObjectType::Schema => "SCHEMA",
1035010397
DescribeObjectType::Task => "TASK",
1035110398
DescribeObjectType::Stage => "STAGE",
10399+
DescribeObjectType::Stream => "STREAM",
1035210400
})
1035310401
}
1035410402
}
@@ -13021,6 +13069,28 @@ impl fmt::Display for AlterTaskAction {
1302113069
}
1302213070
}
1302313071

13072+
/// Action for [`Statement::AlterStream`].
13073+
///
13074+
/// See <https://docs.snowflake.com/en/sql-reference/sql/alter-stream>.
13075+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
13076+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13077+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
13078+
pub enum AlterStreamOperation {
13079+
/// `SET COMMENT = '<string>'`
13080+
SetComment(String),
13081+
/// `UNSET COMMENT`
13082+
UnsetComment,
13083+
}
13084+
13085+
impl fmt::Display for AlterStreamOperation {
13086+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13087+
match self {
13088+
AlterStreamOperation::SetComment(value) => write!(f, "SET COMMENT = '{value}'"),
13089+
AlterStreamOperation::UnsetComment => write!(f, "UNSET COMMENT"),
13090+
}
13091+
}
13092+
}
13093+
1302413094
/// The `CATALOG_SOURCE` of a `CATALOG INTEGRATION`.
1302513095
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1302613096
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

src/ast/spans.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,8 +542,10 @@ impl Spanned for Statement {
542542
Statement::ShowAccounts { .. } => Span::empty(),
543543
Statement::CreateTask { .. } => Span::empty(),
544544
Statement::AlterTask { .. } => Span::empty(),
545+
Statement::AlterStream { .. } => Span::empty(),
545546
Statement::ExecuteTask { .. } => Span::empty(),
546547
Statement::ShowTasks { .. } => Span::empty(),
548+
Statement::ShowStreams { .. } => Span::empty(),
547549
Statement::CreateExternalVolume { .. } => Span::empty(),
548550
Statement::AlterExternalVolume { .. } => Span::empty(),
549551
Statement::DropExternalVolume { .. } => Span::empty(),

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,7 @@ define_keywords!(
10641064
STORED,
10651065
STRAIGHT_JOIN,
10661066
STREAM,
1067+
STREAMS,
10671068
STRICT,
10681069
STRING,
10691070
STRUCT,

src/parser/mod.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11459,6 +11459,7 @@ impl<'a> Parser<'a> {
1145911459
Keyword::WAREHOUSE,
1146011460
Keyword::ACCOUNT,
1146111461
Keyword::TASK,
11462+
Keyword::STREAM,
1146211463
])?;
1146311464
match object_type {
1146411465
Keyword::SCHEMA => {
@@ -11510,9 +11511,10 @@ impl<'a> Parser<'a> {
1151011511
Keyword::WAREHOUSE => self.parse_alter_warehouse(),
1151111512
Keyword::ACCOUNT => self.parse_alter_account(),
1151211513
Keyword::TASK => self.parse_alter_task(),
11514+
Keyword::STREAM => self.parse_alter_stream(),
1151311515
// unreachable because expect_one_of_keywords used above
1151411516
unexpected_keyword => Err(ParserError::ParserError(
11515-
format!("Internal parser error: expected any of {{VIEW, TYPE, COLLATION, TABLE, INDEX, FUNCTION, AGGREGATE, ROLE, POLICY, CONNECTOR, ICEBERG, SCHEMA, USER, OPERATOR, WAREHOUSE, ACCOUNT, TASK}}, got {unexpected_keyword:?}"),
11517+
format!("Internal parser error: expected any of {{VIEW, TYPE, COLLATION, TABLE, INDEX, FUNCTION, AGGREGATE, ROLE, POLICY, CONNECTOR, ICEBERG, SCHEMA, USER, OPERATOR, WAREHOUSE, ACCOUNT, TASK, STREAM}}, got {unexpected_keyword:?}"),
1151611518
)),
1151711519
}
1151811520
}
@@ -11849,6 +11851,27 @@ impl<'a> Parser<'a> {
1184911851
})
1185011852
}
1185111853

11854+
/// Parse `ALTER STREAM [IF EXISTS] <name> { SET COMMENT = '<s>' | UNSET COMMENT }`.
11855+
pub fn parse_alter_stream(&mut self) -> Result<Statement, ParserError> {
11856+
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
11857+
let name = self.parse_object_name(false)?;
11858+
let operation = if self.parse_keyword(Keyword::SET) {
11859+
self.expect_keyword(Keyword::COMMENT)?;
11860+
self.expect_token(&Token::Eq)?;
11861+
AlterStreamOperation::SetComment(self.parse_literal_string()?)
11862+
} else if self.parse_keyword(Keyword::UNSET) {
11863+
self.expect_keyword(Keyword::COMMENT)?;
11864+
AlterStreamOperation::UnsetComment
11865+
} else {
11866+
return self.expected("SET or UNSET after ALTER STREAM", self.peek_token());
11867+
};
11868+
Ok(Statement::AlterStream {
11869+
if_exists,
11870+
name,
11871+
operation,
11872+
})
11873+
}
11874+
1185211875
/// Parse a [Statement::AlterTable]
1185311876
pub fn parse_alter_table(&mut self, iceberg: bool) -> Result<Statement, ParserError> {
1185411877
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
@@ -14880,13 +14903,23 @@ impl<'a> Parser<'a> {
1488014903
object_name,
1488114904
});
1488214905
}
14906+
// `DESCRIBE TABLE STREAM <name>` is not valid Snowflake
14907+
// syntax (a client such as sqlglot may emit it). Real
14908+
// Snowflake consumes the whole statement and then reports an
14909+
// unexpected end-of-input; mirror that by draining the name
14910+
// and failing at EOF rather than at the second identifier.
14911+
if self.parse_keywords(&[Keyword::TABLE, Keyword::STREAM]) {
14912+
let _ = self.parse_object_name(false)?;
14913+
return self.expected("end of statement", self.peek_token());
14914+
}
1488314915
if let Some(kw) = self.parse_one_of_keywords(&[
1488414916
Keyword::TABLE,
1488514917
Keyword::VIEW,
1488614918
Keyword::DATABASE,
1488714919
Keyword::SCHEMA,
1488814920
Keyword::TASK,
1488914921
Keyword::STAGE,
14922+
Keyword::STREAM,
1489014923
]) {
1489114924
let object_type = match kw {
1489214925
Keyword::TABLE => DescribeObjectType::Table,
@@ -14895,6 +14928,7 @@ impl<'a> Parser<'a> {
1489514928
Keyword::SCHEMA => DescribeObjectType::Schema,
1489614929
Keyword::TASK => DescribeObjectType::Task,
1489714930
Keyword::STAGE => DescribeObjectType::Stage,
14931+
Keyword::STREAM => DescribeObjectType::Stream,
1489814932
_ => return self.expected("a describe object type", self.peek_token()),
1489914933
};
1490014934
let object_name = self.parse_object_name(false)?;
@@ -16403,6 +16437,8 @@ impl<'a> Parser<'a> {
1640316437
Ok(self.parse_show_tables(terse, extended, full, external)?)
1640416438
} else if self.parse_keyword(Keyword::TASKS) {
1640516439
Ok(self.parse_show_tasks(terse)?)
16440+
} else if self.parse_keyword(Keyword::STREAMS) {
16441+
Ok(self.parse_show_streams(terse)?)
1640616442
} else if self.parse_keywords(&[Keyword::MATERIALIZED, Keyword::VIEWS]) {
1640716443
Ok(self.parse_show_views(terse, true)?)
1640816444
} else if self.parse_keyword(Keyword::VIEWS) {
@@ -16557,6 +16593,14 @@ impl<'a> Parser<'a> {
1655716593
})
1655816594
}
1655916595

16596+
fn parse_show_streams(&mut self, terse: bool) -> Result<Statement, ParserError> {
16597+
let show_options = self.parse_show_stmt_options()?;
16598+
Ok(Statement::ShowStreams {
16599+
terse,
16600+
show_options,
16601+
})
16602+
}
16603+
1656016604
fn parse_show_views(
1656116605
&mut self,
1656216606
terse: bool,

0 commit comments

Comments
 (0)