-
-
Notifications
You must be signed in to change notification settings - Fork 195
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
Bug in logic for dropping parentheses #674
Comments
This was the reason the Condition API was introduced, and is the recommended way to build complex / mixed conditional expressions. The reason However as you pointed out, it may be non-trivial to do it properly. |
Thanks for getting back to me quickly!
Which appears sound - most builtin binary operations are parsed in a left associative way in PostgreSQL. Also, the comparison operators are nonassociative, so you have to keep the parentheses. See the additional bug below.
Produces: |
I could make a pull request if you like? Think I have an easy solution. |
The handling of unary expressions also has a serious bug:
|
Well, there is no easy comprehensive solution to these problems, which must be fixed by considering how the associativity and precedence rules for parsing expressions in the respective backends. The idea was to in binary expressions:
Drop right parenthesis if:
And similarly for Unary expressions:
However, the respective backends supported have differing operator precedence rules: What can be done is to order some of those pairs of operators where all backends agree, and have precedence be unknown otherwise. Adding the simple things.. like "*/%+-" greater than the comparison operators and so on. Otherwise SQL construction should know about the backend, which it does not appear to do now, and integer precedence values consistent with the precedence table of the backend can be assigned. |
Thank you for your investigation and suggestion. It seemed very doable! I was wondering, can we further simplify the logic to "we add a paren if the operators are different", without regarding precedence? It might be more verbose, but it would not be wrong. I hope it'd not break too many of our existing test cases though. Btw, the SQL building stage is database specific, so we can potentially have different logic for each backend. Though it is preferable if we can align them. |
I think I changed my mind about needing DB specific information, since for the human SQL reader, you would essentially need to know the particularities of precedence rules for your DB to decide what the expression means. Better to go safe and readable and use some parentheses. So I did as you suggested above and added paren if operators are different by default, but I also added some cases where parens can be dropped to cover exceptional situations and some broad precedence cases - otherwise one third of the tests broke, and the SQL was quite ugly. Had a go at a pull request, let me know what you think. |
…ndled as SimpleExpr:Binary
This code causes a panic I believe:
Please fix this soon; this is a serious bug. We are building queries dynamically and cannot simply "switch" to using Edit: Sorry, forgot to post the location of the panic. This is the stack trace:
And the relevant lines are: let right_paren = (right.need_parentheses()
|| right.is_binary() && op != left.get_bin_oper().unwrap())
--------------------------------------------------- ^ unwrap called on None
&& !no_right_paren
&& !no_paren; |
@prakol16 can you post the location and reason for the panic? |
I edited my original comment. |
Edit:I think this is fixed as a consequence of by bugfix in #675 (edited with correct PR) |
Issue #657 seems to be about I agree #675 likely solves this issue. I just wanted to point out that this isn't just a soundness issue -- it can cause crashes/panics as well. I think this puts a little pressure to get the PR merged and released sooner. Test case:
I believe this is correct. Another correct option (with more parentheses) includes |
Thanks for the test case! Put it into the PR. Strictly we can drop the parentheses around the right hand expression here. |
* Issue #674: Fix issues with semantics of parenthesis removal * Fix comment * #674 Revamp how conditions are written to SQL - now uniformly handled as SimpleExpr:Binary * #674 Fix clippy * Issue #674 Move methods for deciding associativity and precedence to trait * Added extra testcase from Issue #674 * Issue #674: Follow up on comments in code review
Can we get a release for this @tyt2y3 I think we hit a bug in our codebase due to that too... |
Sure. I am planning to get on it tmr. |
Backported the fixes to 0.30.1 |
@tyt2y3 Could we have a "give me all the parentheses" mode (either feature flag or runtime). |
Yeah, that's fair. I think it is better to be a runtime option instead of a feature flag. |
Description
From https://github.com/SeaQL/sea-query/blob/master/src/expr.rs:
This logic appears flawed, since the expression
(("a" OR "b") OR "c") is determined to not need a parenthesis.
But it clearly does some times:
((("a" OR "b") OR "c") AND "d")
In binary_expr() (a method of QueryBuilder), need_parenthesis()=false is sufficient to drop them, but SimpleExpr really does not have enough information to be able to decide that.
Steps to Reproduce
Outputs:
Needs parentheses: false
Sql: "a" OR "b" OR "c" AND "d"
But should be ("a" OR "b" OR "c") AND "d" or equivalent.
Expected Behavior
Expect parentheses to preserve semantics of expressions.
Actual Behavior
Parentheses are eliminated in a way that changes the semantics of the expression.
Reproduces How Often
Deterministic.
Versions
v0.30.0
DB / OS not relevant.
Additional Information
See test case above.
The text was updated successfully, but these errors were encountered: