mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-10-09 13:40:22 +00:00
feat: implement select * ilike for snowflake (#1228)
This commit is contained in:
parent
d1f67bdc47
commit
4604628c43
6 changed files with 100 additions and 3 deletions
|
@ -40,8 +40,8 @@ pub use self::ddl::{
|
|||
pub use self::operator::{BinaryOperator, UnaryOperator};
|
||||
pub use self::query::{
|
||||
Cte, CteAsMaterialized, Distinct, ExceptSelectItem, ExcludeSelectItem, Fetch, ForClause,
|
||||
ForJson, ForXml, GroupByExpr, IdentWithAlias, Join, JoinConstraint, JoinOperator,
|
||||
JsonTableColumn, JsonTableColumnErrorHandling, LateralView, LockClause, LockType,
|
||||
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,
|
||||
|
|
|
@ -474,6 +474,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...]`.
|
||||
|
@ -489,6 +492,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}")?;
|
||||
}
|
||||
|
@ -505,6 +511,29 @@ 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: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for IlikeSelectItem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"ILIKE '{}'",
|
||||
value::escape_single_quote_string(&self.pattern)
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/// Snowflake `EXCLUDE` information.
|
||||
///
|
||||
/// # Syntax
|
||||
|
|
|
@ -9018,7 +9018,13 @@ impl<'a> Parser<'a> {
|
|||
pub fn parse_wildcard_additional_options(
|
||||
&mut self,
|
||||
) -> Result<WildcardAdditionalOptions, ParserError> {
|
||||
let opt_exclude = if dialect_of!(self is GenericDialect | DuckDbDialect | SnowflakeDialect)
|
||||
let opt_ilike = if dialect_of!(self is GenericDialect | SnowflakeDialect) {
|
||||
self.parse_optional_select_item_ilike()?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let opt_exclude = if opt_ilike.is_none()
|
||||
&& dialect_of!(self is GenericDialect | DuckDbDialect | SnowflakeDialect)
|
||||
{
|
||||
self.parse_optional_select_item_exclude()?
|
||||
} else {
|
||||
|
@ -9044,6 +9050,7 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
|
||||
Ok(WildcardAdditionalOptions {
|
||||
opt_ilike,
|
||||
opt_exclude,
|
||||
opt_except,
|
||||
opt_rename,
|
||||
|
@ -9051,6 +9058,25 @@ impl<'a> Parser<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Parse an [`Ilike`](IlikeSelectItem) information for wildcard select items.
|
||||
///
|
||||
/// If it is not possible to parse it, will return an option.
|
||||
pub fn parse_optional_select_item_ilike(
|
||||
&mut self,
|
||||
) -> Result<Option<IlikeSelectItem>, ParserError> {
|
||||
let opt_ilike = if self.parse_keyword(Keyword::ILIKE) {
|
||||
let next_token = self.next_token();
|
||||
let pattern = match next_token.token {
|
||||
Token::SingleQuotedString(s) => s,
|
||||
_ => return self.expected("ilike pattern", next_token),
|
||||
};
|
||||
Some(IlikeSelectItem { 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.
|
||||
|
|
|
@ -6622,6 +6622,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,
|
||||
|
|
|
@ -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,
|
||||
|
@ -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,
|
||||
|
|
|
@ -1555,3 +1555,42 @@ fn parse_comma_outer_join() {
|
|||
fn test_sf_trailing_commas() {
|
||||
snowflake().verified_only_select_with_canonical("SELECT 1, 2, FROM t", "SELECT 1, 2 FROM t");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_wildcard_with_ilike() {
|
||||
let select = snowflake_and_generic().verified_only_select(r#"SELECT * ILIKE '%id%' FROM tbl"#);
|
||||
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
|
||||
opt_ilike: Some(IlikeSelectItem {
|
||||
pattern: "%id%".to_owned(),
|
||||
}),
|
||||
..Default::default()
|
||||
});
|
||||
assert_eq!(expected, select.projection[0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_wildcard_with_ilike_double_quote() {
|
||||
let res = snowflake().parse_sql_statements(r#"SELECT * ILIKE "%id" FROM tbl"#);
|
||||
assert_eq!(
|
||||
res.unwrap_err().to_string(),
|
||||
"sql parser error: Expected ilike pattern, found: \"%id\""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_wildcard_with_ilike_number() {
|
||||
let res = snowflake().parse_sql_statements(r#"SELECT * ILIKE 42 FROM tbl"#);
|
||||
assert_eq!(
|
||||
res.unwrap_err().to_string(),
|
||||
"sql parser error: Expected ilike pattern, found: 42"
|
||||
);
|
||||
}
|
||||
|
||||
#[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: Expected end of statement, found: EXCLUDE"
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue