-
Notifications
You must be signed in to change notification settings - Fork 122
Fix mathematical, bitwise and logical operator precedence in SQL #4221
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1221,12 +1221,16 @@ namedFunctionArg | |||||||||||||||||||||||||||||||||||||||||||||
| ; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Expressions, predicates | ||||||||||||||||||||||||||||||||||||||||||||||
| // Mathemtical and logical expressions must be listed as different alternatives to the left-recursive | ||||||||||||||||||||||||||||||||||||||||||||||
| // rules to represent the precedence of the underlying operators. | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| expression | ||||||||||||||||||||||||||||||||||||||||||||||
| : notOperator=(NOT | '!') expression #notExpression // done | ||||||||||||||||||||||||||||||||||||||||||||||
| : notOperator=(NOT | EXCLAMATION_SYMBOL) expression #notExpression // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | EXISTS '(' query ')' #existsExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | expressionAtom predicate? #predicatedExpression | ||||||||||||||||||||||||||||||||||||||||||||||
| | expression logicalOperator expression #logicalExpression // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | left=expressionAtom comparisonOperator right=expressionAtom #binaryComparisonExpression // done | ||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these be |
||||||||||||||||||||||||||||||||||||||||||||||
| | left=expression operator=(AND | LOGICAL_AND_AND) right=expression #logicalExpression // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | left=expression operator=(XOR | OR | LOGICAL_OR_OR) expression #logicalExpression // done | ||||||||||||||||||||||||||||||||||||||||||||||
| ; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| predicate | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1237,16 +1241,19 @@ predicate | |||||||||||||||||||||||||||||||||||||||||||||
| ; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| expressionAtom | ||||||||||||||||||||||||||||||||||||||||||||||
| : constant #constantExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | fullColumnName #fullColumnNameExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | functionCall #functionCallExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | preparedStatementParameter #preparedStatementParameterAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | recordConstructor #recordConstructorExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | arrayConstructor #arrayConstructorExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | base=expressionAtom LEFT_SQUARE_BRACKET index=expressionAtom RIGHT_SQUARE_BRACKET #subscriptExpression // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | left=expressionAtom bitOperator right=expressionAtom #bitExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | left=expressionAtom mathOperator right=expressionAtom #mathExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | left=expressionAtom comparisonOperator right=expressionAtom #binaryComparisonPredicate // done | ||||||||||||||||||||||||||||||||||||||||||||||
| : constant #constantExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | fullColumnName #fullColumnNameExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | functionCall #functionCallExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | preparedStatementParameter #preparedStatementParameterAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | recordConstructor #recordConstructorExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | arrayConstructor #arrayConstructorExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | base=expressionAtom LEFT_SQUARE_BRACKET index=expressionAtom RIGHT_SQUARE_BRACKET #subscriptExpression // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | left=expressionAtom operator=(BIT_SHIFT_LEFT_OP | BIT_SHIFT_RIGHT_OP) right=expressionAtom #bitExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | left=expressionAtom operator=BIT_AND_OP right=expressionAtom #bitExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | left=expressionAtom operator=(BIT_XOR_OP | BIT_OR_OP) right=expressionAtom #bitExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1251
to
+1253
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hatyo Do you know if it is intentional that we used to have a higher precedence for bitwise operators over arithmetic operators? I think most languages (with few exceptions like Pascal) give a higher precedence to arithmetic operators so the expression Lines 327 to 348 in 9d21654
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is not intentional, put differently, we did not make an explicit decision to give bitwise operations higher precedence over the other operators. The referenced unit test does not verify precedence, it just verifies that plan constraints are created exactly as they should be. That's all.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I mean sure, but we should align with DB vendors (since bitwise operators are not part of the SQL standard) who for the most part, align with programming languages operator precedence conventions. Bitwise operators are usually lower than basic arithmetic operator, and higher than logical operators. We should align with that. For example in PgSQL: SELECT
1 + 3 & 5 AS actual, -- 4 (proves + binds first)
(1 + 3) & 5 AS arith_first, -- 4 (same as actual)
1 + (3 & 5) AS bitwise_first -- 2 (different)
;
actual | arith_first | bitwise_first
--------+-------------+---------------
4 | 4 | 2
SELECT
3 & 2 != 0 AS actual, -- true (proves & binds first)
(3 & 2) != 0 AS bitwise_first, -- true (same as actual)
3 & (2 != 0)::int AS comparison_first -- 1 (different)
actual | bitwise_first | comparison_first
--------+---------------+------------------
true | true | 1Doing so, may break some customer queries though, so we have to be extra careful. (once #4199 is ready, we can use it to introduce these illustrative examples). |
||||||||||||||||||||||||||||||||||||||||||||||
| | left=expressionAtom operator=(STAR|DIVIDE) right=expressionAtom #mathExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | left=expressionAtom operator=(MODULO|DIV|MOD) right=expressionAtom #mathExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| | left=expressionAtom operator=(PLUS|MINUS) right=expressionAtom #mathExpressionAtom // done | ||||||||||||||||||||||||||||||||||||||||||||||
| ; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| inList | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1270,18 +1277,6 @@ comparisonOperator | |||||||||||||||||||||||||||||||||||||||||||||
| | IS NOT? DISTINCT FROM | ||||||||||||||||||||||||||||||||||||||||||||||
| ; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| logicalOperator | ||||||||||||||||||||||||||||||||||||||||||||||
| : AND | '&' '&' | XOR | OR | '|' '|' | ||||||||||||||||||||||||||||||||||||||||||||||
| ; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| bitOperator | ||||||||||||||||||||||||||||||||||||||||||||||
| : '<' '<' | '>' '>' | '&' | '^' | '|' | ||||||||||||||||||||||||||||||||||||||||||||||
| ; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| mathOperator | ||||||||||||||||||||||||||||||||||||||||||||||
| : '*' | '/' | '%' | DIV | MOD | '+' | '-' | ||||||||||||||||||||||||||||||||||||||||||||||
| ; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| jsonOperator | ||||||||||||||||||||||||||||||||||||||||||||||
| : '-' '>' | '-' '>' '>' | ||||||||||||||||||||||||||||||||||||||||||||||
| ; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -131,8 +131,10 @@ | |
| .put("cosine_distance", argumentsCount -> BuiltInFunctionCatalog.resolve("cosine_distance", argumentsCount)) | ||
| .put("dot_product_distance", argumentsCount -> BuiltInFunctionCatalog.resolve("dot_product_distance", argumentsCount)) | ||
| .put("not", argumentsCount -> BuiltInFunctionCatalog.resolve("not", argumentsCount)) | ||
| .put("and", argumentsCount -> BuiltInFunctionCatalog.resolve("and", argumentsCount)) | ||
| .put("&&", argumentsCount -> BuiltInFunctionCatalog.resolve("and", argumentsCount)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not seem relevant to this PR, can you please revert it (and line 137)? We can decide later if we want to add support to the non-standard |
||
| .put("or", argumentsCount -> BuiltInFunctionCatalog.resolve("or", argumentsCount)) | ||
| .put("||", argumentsCount -> BuiltInFunctionCatalog.resolve("or", argumentsCount)) | ||
| .put("count", argumentsCount -> BuiltInFunctionCatalog.resolve("COUNT", argumentsCount)) | ||
| .put("max", argumentsCount -> BuiltInFunctionCatalog.resolve("MAX", argumentsCount)) | ||
| .put("min", argumentsCount -> BuiltInFunctionCatalog.resolve("MIN", argumentsCount)) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please rewrite the comment to better illustrate the intentions and guide future modifications to the rules below, something like:
put comparison and logical operators higher so they have less precedence than below bitwise and arithmetic operators. Order of rules matter to leverage inter-rule precedence rules as supported by Antlr4.