diff --git a/ibis/backends/sql/compilers/base.py b/ibis/backends/sql/compilers/base.py index fb2ac89c7fb1..786f67afa6db 100644 --- a/ibis/backends/sql/compilers/base.py +++ b/ibis/backends/sql/compilers/base.py @@ -847,7 +847,7 @@ def visit_Clip(self, op, *, arg, lower, upper): return arg def visit_FloorDivide(self, op, *, left, right): - return self.cast(self.f.floor(left / right), op.dtype) + return self.cast(self.f.floor(sge.paren(left) / sge.paren(right)), op.dtype) def visit_Ceil(self, op, *, arg): return self.cast(self.f.ceil(arg), op.dtype) diff --git a/ibis/backends/sql/compilers/flink.py b/ibis/backends/sql/compilers/flink.py index 10a3b3db0947..f47eb7005c34 100644 --- a/ibis/backends/sql/compilers/flink.py +++ b/ibis/backends/sql/compilers/flink.py @@ -330,7 +330,7 @@ def visit_TryCast(self, op, *, arg, to): return sge.TryCast(this=arg, to=type_mapper.from_ibis(to)) def visit_FloorDivide(self, op, *, left, right): - return self.f.floor(left / right) + return self.f.floor(sge.paren(left) / sge.paren(right)) def visit_JSONGetItem(self, op, *, arg, index): assert isinstance(op.index, ops.Literal) diff --git a/ibis/backends/tests/test_numeric.py b/ibis/backends/tests/test_numeric.py index a40408c7631b..01a04e455416 100644 --- a/ibis/backends/tests/test_numeric.py +++ b/ibis/backends/tests/test_numeric.py @@ -902,6 +902,13 @@ def test_simple_math_functions_columns( backend.assert_series_equal(result, expected) +def test_floor_divide_precedence(con): + # Check that we compile to 16 / (4 / 2) and not 16 / 4 / 2 + expr = ibis.literal(16) // (4 / ibis.literal(2)) + result = int(con.execute(expr)) + assert result == 8 + + # we add one to double_col in this test to make sure the common case works (no # domain errors), and we test the backends' various failure modes in each # backend's test suite