LucaCappelletti94 commented on code in PR #2494:
URL: 
https://github.com/apache/datafusion-sqlparser-rs/pull/2494#discussion_r4093183982


##########
src/parser/mod.rs:
##########


Review Comment:
   You could split the loop out of `parse_subexpr` so that `parse_substring` 
can resume it on the expression it already has.
   
   ```suggestion
           debug!("prefix: {expr:?}");
           self.parse_infix_chain(expr, precedence)
       }
   
       /// Applies infix operators binding tighter than `precedence` to `expr`.
       fn parse_infix_chain(&mut self, mut expr: Expr, precedence: u8) -> 
Result<Expr, ParserError> {
           loop {
               let next_precedence = self.get_next_precedence()?;
               debug!("next precedence: {next_precedence:?}");
   
               if precedence >= next_precedence {
                   break;
               }
   
               // The period operator is handled exclusively by the
               // compound field access parsing.
               if Token::Period == self.peek_token_ref().token {
                   break;
               }
   
               expr = self.parse_infix(expr, next_precedence)?;
           }
           Ok(expr)
       }
   ```



##########
src/parser/mod.rs:
##########
@@ -3041,7 +3044,25 @@ impl<'a> Parser<'a> {
             }
         };
         self.expect_token(&Token::LParen)?;
-        let expr = self.parse_expr()?;
+        // Parse at `LIKE` precedence so a bare `SIMILAR` isn't swallowed as 
the start of a
+        // `SIMILAR TO` operator, allowing it to be recognized as the 
substring syntax below.
+        let expr = 
self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?;
+
+        if self.parse_keyword(Keyword::SIMILAR) {
+            let from_expr = self.parse_expr()?;
+            self.expect_keyword_is(Keyword::ESCAPE)?;
+            let to_expr = self.parse_expr()?;
+            self.expect_token(&Token::RParen)?;
+            return Ok(Expr::Substring {
+                expr: Box::new(expr),
+                substring_from: Some(Box::new(from_expr)),
+                substring_for: Some(Box::new(to_expr)),
+                special: false,
+                shorthand,
+                similar: true,
+            });
+        }

Review Comment:
   You should stop only at a bare `SIMILAR` and then resume the infix chain.
   
   Parsing the source at `LIKE` precedence drops every lower-binding operator, 
so `SUBSTRING(a IS NULL FROM 1)` and `SUBSTRING(a LIKE 'x' FROM 1)` would fail 
in all dialects, and so do `SUBSTRING(1 = 1 FROM 1)` and `SUBSTRING(a ~ 'x' 
FROM 1)` in Postgres.
   
   `SUBSTRING(a SIMILAR TO 'x' FROM 1)` also fails because `SIMILAR TO` gets 
taken for the substring form.
   
   All of these parse on `main`. I would have hoped for the fuzzer to catch 
these, but maybe there are still way too many errors in main before it can 
actually function well enough.
   
   ```suggestion
           // Stop before `SIMILAR` so the substring form can claim it, then 
resume the infix chain.
           let expr = 
self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?;
   
           if self.peek_keyword(Keyword::SIMILAR)
               && !matches!(&self.peek_nth_token_ref(1).token, Token::Word(w) 
if w.keyword == Keyword::TO)
           {
               self.advance_token();
               let from_expr = self.parse_expr()?;
               self.expect_keyword_is(Keyword::ESCAPE)?;
               let to_expr = self.parse_expr()?;
               self.expect_token(&Token::RParen)?;
               return Ok(Expr::Substring {
                   expr: Box::new(expr),
                   substring_from: Some(Box::new(from_expr)),
                   substring_for: Some(Box::new(to_expr)),
                   special: false,
                   shorthand,
                   similar: true,
               });
           }
           let expr = self.parse_infix_chain(expr, 
self.dialect.prec_unknown())?;
   ```



##########
tests/sqlparser_common.rs:
##########
@@ -8425,6 +8425,11 @@ fn parse_substring() {
     verified_stmt("SELECT SUBSTRING('foo' FROM 1 FOR 2) FROM t");
     verified_stmt("SELECT SUBSTR('foo' FROM 1 FOR 2) FROM t");
     verified_stmt("SELECT SUBSTR('foo', 1, 2) FROM t");
+    verified_stmt("SELECT SUBSTRING('1' SIMILAR '_' ESCAPE '#')");

Review Comment:
   You may want to pin both regressions. On the current head the test fails 
with `Expected: ), found: IS`.
   
   ```suggestion
       verified_stmt("SELECT SUBSTRING('1' SIMILAR '_' ESCAPE '#')");
       verified_stmt("SELECT SUBSTRING(a IS NULL FROM 1)");
       verified_stmt("SELECT SUBSTRING(a SIMILAR TO 'x' FROM 1)");
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to