Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement wildcard select ilike #22

Merged
merged 7 commits into from
Apr 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ pub use self::ddl::{
pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
ConnectBy, Cte, CteAsMaterialized, Distinct, ExceptSelectItem, ExcludeSelectItem, Fetch,
ForClause, ForJson, ForXml, GroupByExpr, IdentWithAlias, Join, JoinConstraint, JoinOperator,
JsonTableColumn, JsonTableColumnErrorHandling, LateralView, LockClause, LockType,
ForClause, ForJson, ForXml, GroupByExpr, IdentWithAlias, IlikeSelectItem, Join, JoinConstraint,
JoinOperator, JsonTableColumn, JsonTableColumnErrorHandling, LateralView, LockClause, LockType,
NamedWindowDefinition, NonBlock, Offset, OffsetRows, OrderByExpr, Query, RenameSelectItem,
ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SetOperator,
SetQuantifier, Table, TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity,
Expand Down
25 changes: 25 additions & 0 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,9 @@ impl fmt::Display for IdentWithAlias {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct WildcardAdditionalOptions {
/// `[ILIKE...]`.
/// Snowflake syntax: <https://docs.snowflake.com/en/sql-reference/sql/select>
pub opt_ilike: Option<IlikeSelectItem>,
/// `[EXCLUDE...]`.
pub opt_exclude: Option<ExcludeSelectItem>,
/// `[EXCEPT...]`.
Expand All @@ -492,6 +495,9 @@ pub struct WildcardAdditionalOptions {

impl fmt::Display for WildcardAdditionalOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ilike) = &self.opt_ilike {
write!(f, " {ilike}")?;
}
if let Some(exclude) = &self.opt_exclude {
write!(f, " {exclude}")?;
}
Expand All @@ -508,6 +514,25 @@ impl fmt::Display for WildcardAdditionalOptions {
}
}

/// Snowflake `ILIKE` information.
///
/// # Syntax
/// ```plaintext
/// ILIKE <value>
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct IlikeSelectItem {
pub pattern: Expr,
HiranmayaGundu marked this conversation as resolved.
Show resolved Hide resolved
}

impl fmt::Display for IlikeSelectItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ILIKE {}", self.pattern)?;
Ok(())
}
}
/// Snowflake `EXCLUDE` information.
///
/// # Syntax
Expand Down
26 changes: 25 additions & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8775,9 +8775,14 @@ impl<'a> Parser<'a> {
pub fn parse_wildcard_additional_options(
&mut self,
) -> Result<WildcardAdditionalOptions, ParserError> {
let opt_ilike = if dialect_of!(self is GenericDialect | SnowflakeDialect) {
self.parse_optional_select_item_ilike()?
} else {
None
};
let opt_exclude = if dialect_of!(self is GenericDialect | DuckDbDialect | SnowflakeDialect)
{
self.parse_optional_select_item_exclude()?
self.parse_optional_select_item_exclude(opt_ilike.is_some())?
HiranmayaGundu marked this conversation as resolved.
Show resolved Hide resolved
} else {
None
};
Expand All @@ -8801,20 +8806,39 @@ impl<'a> Parser<'a> {
};

Ok(WildcardAdditionalOptions {
opt_ilike,
opt_exclude,
opt_except,
opt_rename,
opt_replace,
})
}

pub fn parse_optional_select_item_ilike(
&mut self,
) -> Result<Option<IlikeSelectItem>, ParserError> {
let opt_ilike = if self.parse_keyword(Keyword::ILIKE) {
let pattern = self.parse_value()?;
HiranmayaGundu marked this conversation as resolved.
Show resolved Hide resolved
Some(IlikeSelectItem {
pattern: Expr::Value(pattern),
})
} else {
None
};
Ok(opt_ilike)
}

/// Parse an [`Exclude`](ExcludeSelectItem) information for wildcard select items.
///
/// If it is not possible to parse it, will return an option.
pub fn parse_optional_select_item_exclude(
&mut self,
opt_ilike: bool,
) -> Result<Option<ExcludeSelectItem>, ParserError> {
let opt_exclude = if self.parse_keyword(Keyword::EXCLUDE) {
if opt_ilike {
return Err(ParserError::ParserError("Unexpected EXCLUDE".to_string()));
}
if self.consume_token(&Token::LParen) {
let columns =
self.parse_comma_separated(|parser| parser.parse_identifier(false))?;
Expand Down
1 change: 1 addition & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6560,6 +6560,7 @@ fn lateral_function() {
distinct: None,
top: None,
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
opt_ilike: None,
opt_exclude: None,
opt_except: None,
opt_rename: None,
Expand Down
2 changes: 2 additions & 0 deletions tests/sqlparser_duckdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ fn test_select_union_by_name() {
distinct: None,
top: None,
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
opt_ilike: None,
opt_exclude: None,
opt_except: None,
opt_rename: None,
Expand Down Expand Up @@ -183,6 +184,7 @@ fn test_select_union_by_name() {
distinct: None,
top: None,
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
opt_ilike: None,
opt_exclude: None,
opt_except: None,
opt_rename: None,
Expand Down
21 changes: 21 additions & 0 deletions tests/sqlparser_snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1615,3 +1615,24 @@ fn test_select_wildcard_with_replace() {
});
assert_eq!(expected, select.projection[0]);
}

#[test]
fn test_select_wildcard_with_ilike() {
let select = snowflake_and_generic().verified_only_select(r#"SELECT * ILIKE '%id%' FROM tbl"#);
HiranmayaGundu marked this conversation as resolved.
Show resolved Hide resolved
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
opt_ilike: Some(IlikeSelectItem {
pattern: Expr::Value(Value::SingleQuotedString("%id%".to_owned())),
}),
..Default::default()
});
assert_eq!(expected, select.projection[0]);
}

#[test]
fn test_select_wildcard_with_ilike_replace() {
let res = snowflake().parse_sql_statements(r#"SELECT * ILIKE '%id%' EXCLUDE col FROM tbl"#);
assert_eq!(
res.unwrap_err().to_string(),
"sql parser error: Unexpected EXCLUDE"
);
}
Loading