Skip to content

Commit 9bce54f

Browse files
authored
Merge pull request #2 from getsynq/zdenko/snowflake-parse-trim-with-second-param-2
# Why We are getting errors with parsing snowflake `TRIM` expression including removing characters separated by comma. https://docs.snowflake.com/en/sql-reference/functions/trim
2 parents 9ac68d1 + 0398ed5 commit 9bce54f

File tree

6 files changed

+103
-98
lines changed

6 files changed

+103
-98
lines changed

.github/workflows/rust.yml

Lines changed: 0 additions & 98 deletions
This file was deleted.

.github/workflows/test.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Test Suite
2+
on:
3+
push:
4+
branches:
5+
- master
6+
pull_request:
7+
types: [opened, synchronize]
8+
9+
jobs:
10+
check:
11+
name: Check
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v2
15+
- uses: actions-rs/toolchain@v1
16+
with:
17+
profile: minimal
18+
toolchain: stable
19+
override: true
20+
- uses: actions-rs/cargo@v1
21+
with:
22+
command: check
23+
24+
test:
25+
name: Test Suite
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v2
29+
- uses: actions-rs/toolchain@v1
30+
with:
31+
profile: minimal
32+
toolchain: stable
33+
override: true
34+
- uses: actions-rs/cargo@v1
35+
with:
36+
command: test

src/ast/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,12 +584,14 @@ pub enum Expr {
584584
/// ```sql
585585
/// TRIM([BOTH | LEADING | TRAILING] [<expr> FROM] <expr>)
586586
/// TRIM(<expr>)
587+
/// TRIM(<expr>, [, characters]) -- only Snowflake
587588
/// ```
588589
Trim {
589590
expr: Box<Expr>,
590591
// ([BOTH | LEADING | TRAILING]
591592
trim_where: Option<TrimWhereField>,
592593
trim_what: Option<Box<Expr>>,
594+
trim_characters: Option<Vec<Expr>>,
593595
},
594596
/// ```sql
595597
/// OVERLAY(<expr> PLACING <expr> FROM <expr>[ FOR <expr> ]
@@ -984,6 +986,7 @@ impl fmt::Display for Expr {
984986
expr,
985987
trim_where,
986988
trim_what,
989+
trim_characters
987990
} => {
988991
write!(f, "TRIM(")?;
989992
if let Some(ident) = trim_where {
@@ -994,6 +997,9 @@ impl fmt::Display for Expr {
994997
} else {
995998
write!(f, "{expr}")?;
996999
}
1000+
if let Some(characters) = trim_characters {
1001+
write!(f, ", {}", display_comma_separated(characters))?;
1002+
}
9971003

9981004
write!(f, ")")
9991005
}

src/parser.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,6 +1254,7 @@ impl<'a> Parser<'a> {
12541254
/// ```sql
12551255
/// TRIM ([WHERE] ['text' FROM] 'text')
12561256
/// TRIM ('text')
1257+
/// TRIM(<expr>, [, characters]) -- only Snowflake
12571258
/// ```
12581259
pub fn parse_trim_expr(&mut self) -> Result<Expr, ParserError> {
12591260
self.expect_token(&Token::LParen)?;
@@ -1275,13 +1276,24 @@ impl<'a> Parser<'a> {
12751276
expr: Box::new(expr),
12761277
trim_where,
12771278
trim_what: Some(trim_what),
1279+
trim_characters: None,
1280+
})
1281+
} else if self.consume_token(&Token::Comma) && dialect_of!(self is SnowflakeDialect) {
1282+
let characters = self.parse_comma_separated(Parser::parse_expr)?;
1283+
self.expect_token(&Token::RParen)?;
1284+
Ok(Expr::Trim {
1285+
expr: Box::new(expr),
1286+
trim_where: None,
1287+
trim_what: None,
1288+
trim_characters: Some(characters),
12781289
})
12791290
} else {
12801291
self.expect_token(&Token::RParen)?;
12811292
Ok(Expr::Trim {
12821293
expr: Box::new(expr),
12831294
trim_where,
12841295
trim_what: None,
1296+
trim_characters: None,
12851297
})
12861298
}
12871299
}

tests/sqlparser_common.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4967,6 +4967,30 @@ fn parse_trim() {
49674967
ParserError::ParserError("Expected ), found: 'xyz'\nNear `SELECT TRIM(FOO`".to_owned()),
49684968
parse_sql_statements("SELECT TRIM(FOO 'xyz' FROM 'xyzfooxyz')").unwrap_err()
49694969
);
4970+
4971+
//keep Snowflake TRIM syntax failing
4972+
let all_expected_snowflake = TestedDialects {
4973+
dialects: vec![
4974+
Box::new(GenericDialect {}),
4975+
Box::new(PostgreSqlDialect {}),
4976+
Box::new(MsSqlDialect {}),
4977+
Box::new(AnsiDialect {}),
4978+
//Box::new(SnowflakeDialect {}),
4979+
Box::new(HiveDialect {}),
4980+
Box::new(RedshiftSqlDialect {}),
4981+
Box::new(MySqlDialect {}),
4982+
Box::new(BigQueryDialect {}),
4983+
Box::new(SQLiteDialect {}),
4984+
Box::new(DuckDbDialect {}),
4985+
],
4986+
options: None,
4987+
};
4988+
assert_eq!(
4989+
ParserError::ParserError("Expected ), found: 'a'\nNear `SELECT TRIM('xyz',`".to_owned()),
4990+
all_expected_snowflake
4991+
.parse_sql_statements("SELECT TRIM('xyz', 'a')")
4992+
.unwrap_err()
4993+
);
49704994
}
49714995

49724996
#[test]

tests/sqlparser_snowflake.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,3 +1053,28 @@ fn test_snowflake_stage_object_names() {
10531053
}
10541054
}
10551055
}
1056+
1057+
#[test]
1058+
fn test_snowflake_trim() {
1059+
let real_sql = r#"SELECT customer_id, TRIM(sub_items.value:item_price_id, '"', "a") AS item_price_id FROM models_staging.subscriptions"#;
1060+
assert_eq!(snowflake().verified_stmt(real_sql).to_string(), real_sql);
1061+
1062+
let sql_only_select = "SELECT TRIM('xyz', 'a')";
1063+
let select = snowflake().verified_only_select(sql_only_select);
1064+
assert_eq!(
1065+
&Expr::Trim {
1066+
expr: Box::new(Expr::Value(Value::SingleQuotedString("xyz".to_owned()))),
1067+
trim_where: None,
1068+
trim_what: None,
1069+
trim_characters: Some(vec![Expr::Value(Value::SingleQuotedString("a".to_owned()))]),
1070+
},
1071+
expr_from_projection(only(&select.projection))
1072+
);
1073+
1074+
// missing comma separation
1075+
let error_sql = "SELECT TRIM('xyz' 'a')";
1076+
assert_eq!(
1077+
ParserError::ParserError("Expected ), found: 'a'\nNear `SELECT TRIM('xyz'`".to_owned()),
1078+
snowflake().parse_sql_statements(error_sql).unwrap_err()
1079+
);
1080+
}

0 commit comments

Comments
 (0)