agent_patch
runs/2026-02-28__18-27-22__gpt-5-4/flux-pr-1501/flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/agent-logs/agent.patch
38606 bytes
diff --git a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/mod.rs b/app/src/ast/mod.rs
index 81bddcd..cf7ec8b 100644
--- a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -2778,36 +2778,30 @@ pub enum 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']
/// ```
- ShowDatabases { filter: Option<ShowStatementFilter> },
+ ShowDatabases { show_options: ShowStatementOptions },
/// ```sql
/// SHOW SCHEMAS [LIKE 'pattern']
/// ```
- ShowSchemas { filter: Option<ShowStatementFilter> },
+ ShowSchemas { show_options: ShowStatementOptions },
/// ```sql
/// SHOW TABLES
/// ```
ShowTables {
extended: bool,
full: bool,
- clause: Option<ShowClause>,
- db_name: Option<Ident>,
- filter: Option<ShowStatementFilter>,
+ show_options: ShowStatementOptions,
},
/// ```sql
/// SHOW VIEWS
/// ```
ShowViews {
materialized: bool,
- clause: Option<ShowClause>,
- db_name: Option<Ident>,
- filter: Option<ShowStatementFilter>,
+ show_options: ShowStatementOptions,
},
/// ```sql
/// SHOW COLLATION
@@ -4387,78 +4381,77 @@ 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}{terse}COLUMNS{history}",
extended = if *extended { "EXTENDED " } else { "" },
full = if *full { "FULL " } else { "" },
- table_name = table_name,
+ terse = if show_options.terse { "TERSE " } else { "" },
+ history = if show_options.history { " HISTORY" } else { "" },
)?;
- if let Some(filter) = filter {
- write!(f, " {filter}")?;
+ if !show_options.is_empty_postfix() {
+ write!(f, " {show_options}")?;
}
Ok(())
}
- Statement::ShowDatabases { filter } => {
- write!(f, "SHOW DATABASES")?;
- if let Some(filter) = filter {
- write!(f, " {filter}")?;
+ Statement::ShowDatabases { show_options } => {
+ write!(
+ f,
+ "SHOW {terse}DATABASES{history}",
+ terse = if show_options.terse { "TERSE " } else { "" },
+ history = if show_options.history { " HISTORY" } else { "" },
+ )?;
+ if !show_options.is_empty_postfix() {
+ write!(f, " {show_options}")?;
}
Ok(())
}
- Statement::ShowSchemas { filter } => {
- write!(f, "SHOW SCHEMAS")?;
- if let Some(filter) = filter {
- write!(f, " {filter}")?;
+ Statement::ShowSchemas { show_options } => {
+ write!(
+ f,
+ "SHOW {terse}SCHEMAS{history}",
+ terse = if show_options.terse { "TERSE " } else { "" },
+ history = if show_options.history { " HISTORY" } else { "" },
+ )?;
+ if !show_options.is_empty_postfix() {
+ write!(f, " {show_options}")?;
}
Ok(())
}
Statement::ShowTables {
extended,
full,
- clause: show_clause,
- db_name,
- filter,
+ show_options,
} => {
write!(
f,
- "SHOW {extended}{full}TABLES",
+ "SHOW {extended}{full}{terse}{external}TABLES{history}",
extended = if *extended { "EXTENDED " } else { "" },
full = if *full { "FULL " } else { "" },
+ terse = if show_options.terse { "TERSE " } else { "" },
+ external = if show_options.external { "EXTERNAL " } else { "" },
+ history = if show_options.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}")?;
+ if !show_options.is_empty_postfix() {
+ write!(f, " {show_options}")?;
}
Ok(())
}
Statement::ShowViews {
materialized,
- clause: show_clause,
- db_name,
- filter,
+ show_options,
} => {
write!(
f,
- "SHOW {}VIEWS",
- if *materialized { "MATERIALIZED " } else { "" }
+ "SHOW {terse}{}{history}",
+ if *materialized { "MATERIALIZED VIEWS" } else { "VIEWS" },
+ terse = if show_options.terse { "TERSE " } else { "" },
+ history = if show_options.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}")?;
+ if !show_options.is_empty_postfix() {
+ write!(f, " {show_options}")?;
}
Ok(())
}
@@ -6169,17 +6162,92 @@ impl fmt::Display for ShowStatementFilter {
}
}
+#[derive(Debug, Default, 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 filter: Option<ShowStatementFilter>,
+ pub filter_position: Option<ShowStatementFilterPosition>,
+ pub starts_with: Option<String>,
+ pub limit: Option<ShowStatementLimit>,
+ pub terse: bool,
+ pub history: bool,
+ pub external: bool,
+}
+
+impl ShowStatementOptions {
+ pub fn is_empty_postfix(&self) -> bool {
+ self.show_in.is_none()
+ && self.filter.is_none()
+ && self.starts_with.is_none()
+ && self.limit.is_none()
+ }
+}
+
+impl fmt::Display for ShowStatementOptions {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let filter_first =
+ matches!(self.filter_position, Some(ShowStatementFilterPosition::BeforeIn));
+
+ if filter_first {
+ if let Some(filter) = &self.filter {
+ write!(f, "{filter}")?;
+ if self.show_in.is_some() || self.starts_with.is_some() || self.limit.is_some() {
+ write!(f, " ")?;
+ }
+ }
+ if let Some(show_in) = &self.show_in {
+ write!(f, "{show_in}")?;
+ if self.starts_with.is_some() || self.limit.is_some() {
+ write!(f, " ")?;
+ }
+ }
+ } else {
+ if let Some(show_in) = &self.show_in {
+ write!(f, "{show_in}")?;
+ if self.filter.is_some() || self.starts_with.is_some() || self.limit.is_some() {
+ write!(f, " ")?;
+ }
+ }
+ if let Some(filter) = &self.filter {
+ write!(f, "{filter}")?;
+ if self.starts_with.is_some() || self.limit.is_some() {
+ write!(f, " ")?;
+ }
+ }
+ }
+
+ if let Some(starts_with) = &self.starts_with {
+ write!(
+ f,
+ "STARTS WITH '{}'",
+ value::escape_single_quote_string(starts_with)
+ )?;
+ if self.limit.is_some() {
+ write!(f, " ")?;
+ }
+ }
+
+ if let Some(limit) = &self.limit {
+ write!(f, "{limit}")?;
+ }
+
+ 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 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"),
@@ -6187,6 +6255,83 @@ impl fmt::Display for ShowClause {
}
}
+#[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 scope: ShowStatementInScope,
+}
+
+impl fmt::Display for ShowStatementIn {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{} {}", self.clause, self.scope)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum ShowStatementInScope {
+ Account,
+ Database(Option<ObjectName>),
+ Schema(Option<ObjectName>),
+ Table(Option<ObjectName>),
+ View(Option<ObjectName>),
+ Application(ObjectName),
+ ApplicationPackage(ObjectName),
+ Object(ObjectName),
+}
+
+impl fmt::Display for ShowStatementInScope {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Account => write!(f, "ACCOUNT"),
+ Self::Database(None) => write!(f, "DATABASE"),
+ Self::Database(Some(name)) => write!(f, "DATABASE {name}"),
+ Self::Schema(None) => write!(f, "SCHEMA"),
+ Self::Schema(Some(name)) => write!(f, "SCHEMA {name}"),
+ Self::Table(None) => write!(f, "TABLE"),
+ Self::Table(Some(name)) => write!(f, "TABLE {name}"),
+ Self::View(None) => write!(f, "VIEW"),
+ Self::View(Some(name)) => write!(f, "VIEW {name}"),
+ Self::Application(name) => write!(f, "APPLICATION {name}"),
+ Self::ApplicationPackage(name) => write!(f, "APPLICATION PACKAGE {name}"),
+ Self::Object(name) => write!(f, "{name}"),
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum ShowStatementFilterPosition {
+ BeforeIn,
+ AfterIn,
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct ShowStatementLimit {
+ pub rows: u64,
+ pub from: Option<String>,
+}
+
+impl fmt::Display for ShowStatementLimit {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "LIMIT {}", self.rows)?;
+ if let Some(from) = &self.from {
+ write!(
+ f,
+ " FROM '{}'",
+ value::escape_single_quote_string(from)
+ )?;
+ }
+ Ok(())
+ }
+}
+
/// Sqlite specific syntax
///
/// See [Sqlite documentation](https://sqlite.org/lang_conflict.html)
diff --git a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/dialect/mod.rs b/app/src/dialect/mod.rs
index c8c11bc..8d8ab29 100644
--- a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/dialect/mod.rs
+++ b/app/src/dialect/mod.rs
@@ -49,7 +49,7 @@ pub use self::postgresql::PostgreSqlDialect;
pub use self::redshift::RedshiftSqlDialect;
pub use self::snowflake::SnowflakeDialect;
pub use self::sqlite::SQLiteDialect;
-use crate::ast::{ColumnOption, Expr, Statement};
+use crate::ast::{ColumnOption, Expr, ShowStatementFilterPosition, Statement};
pub use crate::keywords;
use crate::keywords::Keyword;
use crate::parser::{Parser, ParserError};
@@ -622,6 +622,12 @@ pub trait Dialect: Debug + Any {
fn supports_boolean_literals(&self) -> bool {
true
}
+
+ /// Controls the canonical ordering of SHOW statement filters relative to
+ /// `IN`/`FROM` clauses when both are present.
+ fn show_statement_filter_position(&self) -> ShowStatementFilterPosition {
+ ShowStatementFilterPosition::AfterIn
+ }
}
/// This represents the operators for which precedence must be defined
diff --git a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/dialect/snowflake.rs b/app/src/dialect/snowflake.rs
index d9331d9..8b4efec 100644
--- a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/dialect/snowflake.rs
+++ b/app/src/dialect/snowflake.rs
@@ -25,7 +25,8 @@ use crate::ast::helpers::stmt_data_loading::{
use crate::ast::{
ColumnOption, ColumnPolicy, ColumnPolicyProperty, Ident, IdentityParameters, IdentityProperty,
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, ObjectName,
- RowAccessPolicy, Statement, TagsColumnOption, WrappedCollection,
+ RowAccessPolicy, ShowStatementFilterPosition, Statement, TagsColumnOption,
+ WrappedCollection,
};
use crate::dialect::{Dialect, Precedence};
use crate::keywords::Keyword;
@@ -96,6 +97,10 @@ impl Dialect for SnowflakeDialect {
true
}
+ fn show_statement_filter_position(&self) -> ShowStatementFilterPosition {
+ ShowStatementFilterPosition::BeforeIn
+ }
+
fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
if parser.parse_keyword(Keyword::CREATE) {
// possibly CREATE STAGE
diff --git a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/parser/mod.rs b/app/src/parser/mod.rs
index d3f4320..9e5f98a 100644
--- a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -9613,24 +9613,31 @@ impl<'a> Parser<'a> {
pub fn parse_show(&mut self) -> Result<Statement, ParserError> {
let extended = self.parse_keyword(Keyword::EXTENDED);
let full = self.parse_keyword(Keyword::FULL);
+ let terse = self.parse_custom_keyword("TERSE");
+ let external = self.parse_keyword(Keyword::EXTERNAL);
let session = self.parse_keyword(Keyword::SESSION);
let global = self.parse_keyword(Keyword::GLOBAL);
if self
.parse_one_of_keywords(&[Keyword::COLUMNS, Keyword::FIELDS])
.is_some()
{
- Ok(self.parse_show_columns(extended, full)?)
+ Ok(self.parse_show_columns(extended, full, terse)?)
} else if self.parse_keyword(Keyword::TABLES) {
- Ok(self.parse_show_tables(extended, full)?)
+ Ok(self.parse_show_tables(extended, full, terse, external)?)
} else if self.parse_keywords(&[Keyword::MATERIALIZED, Keyword::VIEWS]) {
- Ok(self.parse_show_views(true)?)
+ Ok(self.parse_show_views(true, terse)?)
} else if self.parse_keyword(Keyword::VIEWS) {
- Ok(self.parse_show_views(false)?)
+ Ok(self.parse_show_views(false, terse)?)
} else if self.parse_keyword(Keyword::FUNCTIONS) {
Ok(self.parse_show_functions()?)
- } else if extended || full {
+ } else if self.parse_keyword(Keyword::DATABASES) {
+ self.parse_show_databases(terse)
+ } else if self.parse_keyword(Keyword::SCHEMAS) {
+ self.parse_show_schemas(terse)
+ } else if extended || full || terse || external {
Err(ParserError::ParserError(
- "EXTENDED/FULL are not supported with this type of SHOW query".to_string(),
+ "EXTENDED/FULL/TERSE/EXTERNAL are not supported with this type of SHOW query"
+ .to_string(),
))
} else if self.parse_one_of_keywords(&[Keyword::CREATE]).is_some() {
Ok(self.parse_show_create()?)
@@ -9652,10 +9659,6 @@ impl<'a> Parser<'a> {
session,
global,
})
- } else if self.parse_keyword(Keyword::DATABASES) {
- self.parse_show_databases()
- } else if self.parse_keyword(Keyword::SCHEMAS) {
- self.parse_show_schemas()
} else {
Ok(Statement::ShowVariable {
variable: self.parse_identifiers()?,
@@ -9663,16 +9666,20 @@ impl<'a> Parser<'a> {
}
}
- fn parse_show_databases(&mut self) -> Result<Statement, ParserError> {
- Ok(Statement::ShowDatabases {
- filter: self.parse_show_statement_filter()?,
- })
+ fn parse_show_databases(&mut self, terse: bool) -> Result<Statement, ParserError> {
+ let history = self.parse_keyword(Keyword::HISTORY);
+ let mut show_options = self.parse_show_statement_options(false, false)?;
+ show_options.terse = terse;
+ show_options.history = history;
+ Ok(Statement::ShowDatabases { show_options })
}
- fn parse_show_schemas(&mut self) -> Result<Statement, ParserError> {
- Ok(Statement::ShowSchemas {
- filter: self.parse_show_statement_filter()?,
- })
+ fn parse_show_schemas(&mut self, terse: bool) -> Result<Statement, ParserError> {
+ let history = self.parse_keyword(Keyword::HISTORY);
+ let mut show_options = self.parse_show_statement_options(true, true)?;
+ show_options.terse = terse;
+ show_options.history = history;
+ Ok(Statement::ShowSchemas { show_options })
}
pub fn parse_show_create(&mut self) -> Result<Statement, ParserError> {
@@ -9704,24 +9711,52 @@ impl<'a> Parser<'a> {
&mut self,
extended: bool,
full: bool,
+ terse: bool,
) -> Result<Statement, ParserError> {
- self.expect_one_of_keywords(&[Keyword::FROM, Keyword::IN])?;
- let object_name = self.parse_object_name(false)?;
- let table_name = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) {
- Some(_) => {
+ let mut show_options = ShowStatementOptions {
+ terse,
+ ..self.parse_show_statement_options(true, true)?
+ };
+
+ if let Some(ShowStatementIn {
+ clause,
+ scope: ShowStatementInScope::Object(object_name),
+ }) = &mut show_options.show_in
+ {
+ if !dialect_of!(self is SnowflakeDialect) {
+ *clause = ShowStatementInClause::FROM;
+ }
+ if matches!(clause, ShowStatementInClause::FROM | ShowStatementInClause::IN)
+ && self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]).is_some()
+ {
let db_name = vec![self.parse_identifier(false)?];
- let ObjectName(table_name) = object_name;
- let object_name = db_name.into_iter().chain(table_name).collect();
- ObjectName(object_name)
+ let ObjectName(table_name) = object_name.clone();
+ *object_name = ObjectName(db_name.into_iter().chain(table_name).collect());
}
- None => object_name,
- };
- let filter = self.parse_show_statement_filter()?;
+ }
+
+ if show_options.show_in.is_none() && !dialect_of!(self is SnowflakeDialect) {
+ self.expect_one_of_keywords(&[Keyword::FROM, Keyword::IN])?;
+ let object_name = self.parse_object_name(false)?;
+ let table_name = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) {
+ Some(_) => {
+ let db_name = vec![self.parse_identifier(false)?];
+ let ObjectName(table_name) = object_name;
+ let object_name = db_name.into_iter().chain(table_name).collect();
+ ObjectName(object_name)
+ }
+ None => object_name,
+ };
+ show_options.show_in = Some(ShowStatementIn {
+ clause: ShowStatementInClause::FROM,
+ scope: ShowStatementInScope::Object(table_name),
+ });
+ }
+
Ok(Statement::ShowColumns {
extended,
full,
- table_name,
- filter,
+ show_options,
})
}
@@ -9729,34 +9764,29 @@ impl<'a> Parser<'a> {
&mut self,
extended: bool,
full: bool,
+ terse: bool,
+ external: bool,
) -> Result<Statement, ParserError> {
- let (clause, db_name) = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) {
- Some(Keyword::FROM) => (Some(ShowClause::FROM), Some(self.parse_identifier(false)?)),
- Some(Keyword::IN) => (Some(ShowClause::IN), Some(self.parse_identifier(false)?)),
- _ => (None, None),
- };
- let filter = self.parse_show_statement_filter()?;
+ let history = self.parse_keyword(Keyword::HISTORY);
+ let mut show_options = self.parse_show_statement_options(true, true)?;
+ show_options.terse = terse;
+ show_options.external = external;
+ show_options.history = history;
Ok(Statement::ShowTables {
extended,
full,
- clause,
- db_name,
- filter,
+ show_options,
})
}
- fn parse_show_views(&mut self, materialized: bool) -> Result<Statement, ParserError> {
- let (clause, db_name) = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) {
- Some(Keyword::FROM) => (Some(ShowClause::FROM), Some(self.parse_identifier(false)?)),
- Some(Keyword::IN) => (Some(ShowClause::IN), Some(self.parse_identifier(false)?)),
- _ => (None, None),
- };
- let filter = self.parse_show_statement_filter()?;
+ fn parse_show_views(&mut self, materialized: bool, terse: bool) -> Result<Statement, ParserError> {
+ let history = self.parse_keyword(Keyword::HISTORY);
+ let mut show_options = self.parse_show_statement_options(true, true)?;
+ show_options.terse = terse;
+ show_options.history = history;
Ok(Statement::ShowViews {
materialized,
- clause,
- db_name,
- filter,
+ show_options,
})
}
@@ -9793,6 +9823,109 @@ impl<'a> Parser<'a> {
}
}
+ fn parse_show_statement_options(
+ &mut self,
+ allow_scope: bool,
+ allow_object_scope: bool,
+ ) -> Result<ShowStatementOptions, ParserError> {
+ let mut show_options = ShowStatementOptions::default();
+ let filter_position = self.dialect.show_statement_filter_position();
+
+ show_options.filter = self.parse_show_statement_filter()?;
+
+ if allow_scope {
+ show_options.show_in = self.parse_show_statement_in(allow_object_scope)?;
+ }
+
+ if show_options.filter.is_none() {
+ show_options.filter = self.parse_show_statement_filter()?;
+ }
+
+ if show_options.filter.is_some() && show_options.show_in.is_some() {
+ show_options.filter_position = Some(filter_position);
+ }
+
+ show_options.starts_with = self.parse_show_starts_with()?;
+ show_options.limit = self.parse_show_limit()?;
+
+ Ok(show_options)
+ }
+
+ fn parse_show_statement_in(
+ &mut self,
+ allow_object_scope: bool,
+ ) -> Result<Option<ShowStatementIn>, ParserError> {
+ let clause = if self.parse_keyword(Keyword::FROM) {
+ Some(ShowStatementInClause::FROM)
+ } else if self.parse_keyword(Keyword::IN) {
+ Some(ShowStatementInClause::IN)
+ } else {
+ None
+ };
+
+ let Some(clause) = clause else {
+ return Ok(None);
+ };
+
+ let scope = if self.parse_custom_keyword("ACCOUNT") {
+ ShowStatementInScope::Account
+ } else if self.parse_keyword(Keyword::DATABASE) {
+ ShowStatementInScope::Database(self.maybe_parse(|p| p.parse_object_name(false))?)
+ } else if self.parse_keyword(Keyword::SCHEMA) {
+ ShowStatementInScope::Schema(self.maybe_parse(|p| p.parse_object_name(false))?)
+ } else if self.parse_keyword(Keyword::TABLE) {
+ ShowStatementInScope::Table(Some(self.parse_object_name(false)?))
+ } else if self.parse_keyword(Keyword::VIEW) {
+ ShowStatementInScope::View(Some(self.parse_object_name(false)?))
+ } else if self.parse_custom_keyword("APPLICATION") {
+ if self.parse_custom_keyword("PACKAGE") {
+ ShowStatementInScope::ApplicationPackage(self.parse_object_name(false)?)
+ } else {
+ ShowStatementInScope::Application(self.parse_object_name(false)?)
+ }
+ } else if allow_object_scope {
+ ShowStatementInScope::Object(self.parse_object_name(false)?)
+ } else {
+ return self.expected("SHOW scope", self.peek_token());
+ };
+
+ Ok(Some(ShowStatementIn { clause, scope }))
+ }
+
+ fn parse_show_starts_with(&mut self) -> Result<Option<String>, ParserError> {
+ if self.parse_custom_keyword("STARTS") {
+ self.expect_keyword(Keyword::WITH)?;
+ Ok(Some(self.parse_literal_string()?))
+ } else {
+ Ok(None)
+ }
+ }
+
+ fn parse_show_limit(&mut self) -> Result<Option<ShowStatementLimit>, ParserError> {
+ if self.parse_keyword(Keyword::LIMIT) {
+ Ok(Some(ShowStatementLimit {
+ rows: self.parse_literal_uint()?,
+ from: if self.parse_keyword(Keyword::FROM) {
+ Some(self.parse_literal_string()?)
+ } else {
+ None
+ },
+ }))
+ } else {
+ Ok(None)
+ }
+ }
+
+ fn parse_custom_keyword(&mut self, expected: &str) -> bool {
+ match self.peek_token().token {
+ Token::Word(w) if w.value.eq_ignore_ascii_case(expected) => {
+ self.next_token();
+ true
+ }
+ _ => false,
+ }
+ }
+
pub fn parse_use(&mut self) -> Result<Statement, ParserError> {
// Determine which keywords are recognized by the current dialect
let parsed_keyword = if dialect_of!(self is HiveDialect) {
diff --git a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mysql.rs b/app/tests/sqlparser_mysql.rs
index 44b2ac6..b82bd86 100644
--- a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mysql.rs
+++ b/app/tests/sqlparser_mysql.rs
@@ -229,8 +229,13 @@ fn parse_show_columns() {
Statement::ShowColumns {
extended: false,
full: false,
- table_name: table_name.clone(),
- filter: None,
+ show_options: ShowStatementOptions {
+ show_in: Some(ShowStatementIn {
+ clause: ShowStatementInClause::FROM,
+ scope: ShowStatementInScope::Object(table_name.clone()),
+ }),
+ ..Default::default()
+ },
}
);
assert_eq!(
@@ -238,8 +243,16 @@ fn parse_show_columns() {
Statement::ShowColumns {
extended: false,
full: false,
- table_name: ObjectName(vec![Ident::new("mydb"), Ident::new("mytable")]),
- filter: None,
+ show_options: ShowStatementOptions {
+ show_in: Some(ShowStatementIn {
+ clause: ShowStatementInClause::FROM,
+ scope: ShowStatementInScope::Object(ObjectName(vec![
+ Ident::new("mydb"),
+ Ident::new("mytable"),
+ ])),
+ }),
+ ..Default::default()
+ },
}
);
assert_eq!(
@@ -247,8 +260,13 @@ fn parse_show_columns() {
Statement::ShowColumns {
extended: true,
full: false,
- table_name: table_name.clone(),
- filter: None,
+ show_options: ShowStatementOptions {
+ show_in: Some(ShowStatementIn {
+ clause: ShowStatementInClause::FROM,
+ scope: ShowStatementInScope::Object(table_name.clone()),
+ }),
+ ..Default::default()
+ },
}
);
assert_eq!(
@@ -256,8 +274,13 @@ fn parse_show_columns() {
Statement::ShowColumns {
extended: false,
full: true,
- table_name: table_name.clone(),
- filter: None,
+ show_options: ShowStatementOptions {
+ show_in: Some(ShowStatementIn {
+ clause: ShowStatementInClause::FROM,
+ scope: ShowStatementInScope::Object(table_name.clone()),
+ }),
+ ..Default::default()
+ },
}
);
assert_eq!(
@@ -265,8 +288,15 @@ fn parse_show_columns() {
Statement::ShowColumns {
extended: false,
full: false,
- table_name: table_name.clone(),
- filter: Some(ShowStatementFilter::Like("pattern".into())),
+ show_options: ShowStatementOptions {
+ show_in: Some(ShowStatementIn {
+ clause: ShowStatementInClause::FROM,
+ scope: ShowStatementInScope::Object(table_name.clone()),
+ }),
+ filter: Some(ShowStatementFilter::Like("pattern".into())),
+ filter_position: Some(ShowStatementFilterPosition::AfterIn),
+ ..Default::default()
+ },
}
);
assert_eq!(
@@ -274,10 +304,17 @@ fn parse_show_columns() {
Statement::ShowColumns {
extended: false,
full: false,
- table_name,
- filter: Some(ShowStatementFilter::Where(
- mysql_and_generic().verified_expr("1 = 2")
- )),
+ show_options: ShowStatementOptions {
+ show_in: Some(ShowStatementIn {
+ clause: ShowStatementInClause::FROM,
+ scope: ShowStatementInScope::Object(table_name),
+ }),
+ filter: Some(ShowStatementFilter::Where(
+ mysql_and_generic().verified_expr("1 = 2")
+ )),
+ filter_position: Some(ShowStatementFilterPosition::AfterIn),
+ ..Default::default()
+ },
}
);
mysql_and_generic()
@@ -329,9 +366,7 @@ fn parse_show_tables() {
Statement::ShowTables {
extended: false,
full: false,
- clause: None,
- db_name: None,
- filter: None,
+ show_options: ShowStatementOptions::default(),
}
);
assert_eq!(
@@ -339,9 +374,13 @@ fn parse_show_tables() {
Statement::ShowTables {
extended: false,
full: false,
- clause: Some(ShowClause::FROM),
- db_name: Some(Ident::new("mydb")),
- filter: None,
+ show_options: ShowStatementOptions {
+ show_in: Some(ShowStatementIn {
+ clause: ShowStatementInClause::FROM,
+ scope: ShowStatementInScope::Object(ObjectName(vec![Ident::new("mydb")])),
+ }),
+ ..Default::default()
+ },
}
);
assert_eq!(
@@ -349,9 +388,7 @@ fn parse_show_tables() {
Statement::ShowTables {
extended: true,
full: false,
- clause: None,
- db_name: None,
- filter: None,
+ show_options: ShowStatementOptions::default(),
}
);
assert_eq!(
@@ -359,9 +396,7 @@ fn parse_show_tables() {
Statement::ShowTables {
extended: false,
full: true,
- clause: None,
- db_name: None,
- filter: None,
+ show_options: ShowStatementOptions::default(),
}
);
assert_eq!(
@@ -369,9 +404,10 @@ fn parse_show_tables() {
Statement::ShowTables {
extended: false,
full: false,
- clause: None,
- db_name: None,
- filter: Some(ShowStatementFilter::Like("pattern".into())),
+ show_options: ShowStatementOptions {
+ filter: Some(ShowStatementFilter::Like("pattern".into())),
+ ..Default::default()
+ },
}
);
assert_eq!(
@@ -379,11 +415,12 @@ fn parse_show_tables() {
Statement::ShowTables {
extended: false,
full: false,
- clause: None,
- db_name: None,
- filter: Some(ShowStatementFilter::Where(
- mysql_and_generic().verified_expr("1 = 2")
- )),
+ show_options: ShowStatementOptions {
+ filter: Some(ShowStatementFilter::Where(
+ mysql_and_generic().verified_expr("1 = 2")
+ )),
+ ..Default::default()
+ },
}
);
mysql_and_generic().verified_stmt("SHOW TABLES IN mydb");
diff --git a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_snowflake.rs b/app/tests/sqlparser_snowflake.rs
index c17c7b9..1bb6bd5 100644
--- a/tmp/agent-patch-flux-pr-1501.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_snowflake.rs
+++ b/app/tests/sqlparser_snowflake.rs
@@ -114,6 +114,98 @@ fn test_snowflake_create_or_replace_table_copy_grants_cta() {
}
}
+#[test]
+fn test_snowflake_show_databases() {
+ assert_eq!(
+ snowflake().verified_stmt(
+ "SHOW TERSE DATABASES HISTORY LIKE 'demo%' STARTS WITH 'DEMO' LIMIT 10 FROM 'demo1'"
+ ),
+ Statement::ShowDatabases {
+ show_options: ShowStatementOptions {
+ filter: Some(ShowStatementFilter::Like("demo%".into())),
+ starts_with: Some("DEMO".into()),
+ limit: Some(ShowStatementLimit {
+ rows: 10,
+ from: Some("demo1".into()),
+ }),
+ terse: true,
+ history: true,
+ ..Default::default()
+ },
+ }
+ );
+}
+
+#[test]
+fn test_snowflake_show_tables_and_columns() {
+ assert_eq!(
+ snowflake().verified_stmt(
+ "SHOW TERSE EXTERNAL TABLES HISTORY LIKE 'foo%' IN DATABASE my_db STARTS WITH 'FOO' LIMIT 5 FROM 'foo0'"
+ ),
+ Statement::ShowTables {
+ extended: false,
+ full: false,
+ show_options: ShowStatementOptions {
+ show_in: Some(ShowStatementIn {
+ clause: ShowStatementInClause::IN,
+ scope: ShowStatementInScope::Database(Some(ObjectName(vec![Ident::new(
+ "my_db"
+ )]))),
+ }),
+ filter: Some(ShowStatementFilter::Like("foo%".into())),
+ filter_position: Some(ShowStatementFilterPosition::BeforeIn),
+ starts_with: Some("FOO".into()),
+ limit: Some(ShowStatementLimit {
+ rows: 5,
+ from: Some("foo0".into()),
+ }),
+ terse: true,
+ history: true,
+ external: true,
+ },
+ }
+ );
+
+ assert_eq!(
+ snowflake().verified_stmt(
+ "SHOW TERSE COLUMNS LIKE 'id%' IN TABLE my_db.my_table LIMIT 3"
+ ),
+ Statement::ShowColumns {
+ extended: false,
+ full: false,
+ show_options: ShowStatementOptions {
+ show_in: Some(ShowStatementIn {
+ clause: ShowStatementInClause::IN,
+ scope: ShowStatementInScope::Table(Some(ObjectName(vec![
+ Ident::new("my_db"),
+ Ident::new("my_table"),
+ ]))),
+ }),
+ filter: Some(ShowStatementFilter::Like("id%".into())),
+ filter_position: Some(ShowStatementFilterPosition::BeforeIn),
+ limit: Some(ShowStatementLimit {
+ rows: 3,
+ from: None,
+ }),
+ terse: true,
+ ..Default::default()
+ },
+ }
+ );
+}
+
+#[test]
+fn test_snowflake_show_like_is_canonicalized_before_in() {
+ snowflake().one_statement_parses_to(
+ "SHOW TABLES IN DATABASE my_db LIKE 'foo%'",
+ "SHOW TABLES LIKE 'foo%' IN DATABASE my_db",
+ );
+ snowflake().one_statement_parses_to(
+ "SHOW COLUMNS IN TABLE my_table LIKE 'foo%'",
+ "SHOW COLUMNS LIKE 'foo%' IN TABLE my_table",
+ );
+}
+
#[test]
fn test_snowflake_create_table_enable_schema_evolution() {
let sql = "CREATE TABLE my_table (a number) ENABLE_SCHEMA_EVOLUTION=TRUE";