@@ -3183,9 +3183,15 @@ impl<'a> Parser<'a> {
31833183 {
31843184 let expr2 = self.parse_expr()?;
31853185 Ok(Expr::IsNotDistinctFrom(Box::new(expr), Box::new(expr2)))
3186+ } else if let Ok((form, negated)) = self.parse_unicode_is_normalized() {
3187+ Ok(Expr::IsNormalized {
3188+ expr: Box::new(expr),
3189+ form,
3190+ negated,
3191+ })
31863192 } else {
31873193 self.expected(
3188- "[NOT] NULL or TRUE| FALSE or [NOT] DISTINCT FROM after IS",
3194+ "[NOT] NULL | TRUE | FALSE | DISTINCT | [form] NORMALIZED FROM after IS",
31893195 self.peek_token(),
31903196 )
31913197 }
@@ -3850,7 +3856,7 @@ impl<'a> Parser<'a> {
38503856 /// If the current token is the `expected` keyword, consume the token.
38513857 /// Otherwise, return an error.
38523858 ///
3853- // todo deprecate infavor of expected_keyword_is
3859+ // todo deprecate in favor of expected_keyword_is
38543860 pub fn expect_keyword(&mut self, expected: Keyword) -> Result<TokenWithSpan, ParserError> {
38553861 if self.parse_keyword(expected) {
38563862 Ok(self.get_current_token().clone())
@@ -8452,6 +8458,42 @@ impl<'a> Parser<'a> {
84528458 }
84538459 }
84548460
8461+ /// Parse a literal unicode normalization clause
8462+ pub fn parse_unicode_is_normalized(
8463+ &mut self,
8464+ ) -> Result<(Option<NormalizationForm>, bool), ParserError> {
8465+ let neg = self.parse_keyword(Keyword::NOT);
8466+ if self.parse_keyword(Keyword::NORMALIZED) {
8467+ return Ok((None, neg));
8468+ }
8469+ let index = self.index;
8470+ let next_token = self.next_token();
8471+ let normalized_form = if let Token::Word(Word {
8472+ value: ref s,
8473+ quote_style: None,
8474+ keyword: Keyword::NoKeyword,
8475+ }) = next_token.token
8476+ {
8477+ match s.to_uppercase().as_str() {
8478+ "NFC" => Some(NormalizationForm::NFC),
8479+ "NFD" => Some(NormalizationForm::NFD),
8480+ "NFKC" => Some(NormalizationForm::NFKC),
8481+ "NFKD" => Some(NormalizationForm::NFKD),
8482+ _ => {
8483+ self.index = index;
8484+ return self.expected("unicode normalization", next_token);
8485+ }
8486+ }
8487+ } else {
8488+ None
8489+ };
8490+ if self.parse_keyword(Keyword::NORMALIZED) {
8491+ return Ok((normalized_form, neg));
8492+ }
8493+ self.index = index;
8494+ self.expected("unicode normalization", self.peek_token())
8495+ }
8496+
84558497 pub fn parse_enum_values(&mut self) -> Result<Vec<EnumMember>, ParserError> {
84568498 self.expect_token(&Token::LParen)?;
84578499 let values = self.parse_comma_separated(|parser| {
@@ -8957,7 +8999,7 @@ impl<'a> Parser<'a> {
89578999 }
89589000 }
89599001
8960- /// Parse a table object for insetion
9002+ /// Parse a table object for insertion
89619003 /// e.g. `some_database.some_table` or `FUNCTION some_table_func(...)`
89629004 pub fn parse_table_object(&mut self) -> Result<TableObject, ParserError> {
89639005 if self.dialect.supports_insert_table_function() && self.parse_keyword(Keyword::FUNCTION) {
@@ -11867,7 +11909,7 @@ impl<'a> Parser<'a> {
1186711909 } else {
1186811910 let mut name = self.parse_grantee_name()?;
1186911911 if self.consume_token(&Token::Colon) {
11870- // Redshift supports namespace prefix for extenrnal users and groups:
11912+ // Redshift supports namespace prefix for external users and groups:
1187111913 // <Namespace>:<GroupName> or <Namespace>:<UserName>
1187211914 // https://docs.aws.amazon.com/redshift/latest/mgmt/redshift-iam-access-control-native-idp.html
1187311915 let ident = self.parse_identifier()?;
@@ -12863,7 +12905,7 @@ impl<'a> Parser<'a> {
1286312905 Ok(WithFill { from, to, step })
1286412906 }
1286512907
12866- // Parse a set of comma seperated INTERPOLATE expressions (ClickHouse dialect)
12908+ // Parse a set of comma separated INTERPOLATE expressions (ClickHouse dialect)
1286712909 // that follow the INTERPOLATE keyword in an ORDER BY clause with the WITH FILL modifier
1286812910 pub fn parse_interpolations(&mut self) -> Result<Option<Interpolate>, ParserError> {
1286912911 if !self.parse_keyword(Keyword::INTERPOLATE) {
@@ -14372,7 +14414,7 @@ mod tests {
1437214414 assert_eq!(
1437314415 ast,
1437414416 Err(ParserError::ParserError(
14375- "Expected: [NOT] NULL or TRUE| FALSE or [NOT] DISTINCT FROM after IS, found: a at Line: 1, Column: 16"
14417+ "Expected: [NOT] NULL | TRUE | FALSE | DISTINCT | [form] NORMALIZED FROM after IS, found: a at Line: 1, Column: 16"
1437614418 .to_string()
1437714419 ))
1437814420 );
0 commit comments