details:   https://code.tryton.org/python-sql/commit/c42894817c35
branch:    default
user:      Cédric Krier <[email protected]>
date:      Sat Jan 24 09:06:14 2026 +0100
description:
        Add support for array operators
diffstat:

 CHANGELOG                   |   1 +
 sql/operators.py            |  24 +++++++++++++++++++++---
 sql/tests/test_operators.py |  26 ++++++++++++++++++++++----
 3 files changed, 44 insertions(+), 7 deletions(-)

diffs (96 lines):

diff -r 05c585f43f17 -r c42894817c35 CHANGELOG
--- a/CHANGELOG Wed Dec 24 10:37:11 2025 +0100
+++ b/CHANGELOG Sat Jan 24 09:06:14 2026 +0100
@@ -1,3 +1,4 @@
+* Add support for array operators
 * Remove the parentheses around the unary and binary operators
 * Use the ordinal number as aliases for GROUP BY
 * Check the coherence of the aliases of GROUP BY and ORDER BY expressions
diff -r 05c585f43f17 -r c42894817c35 sql/operators.py
--- a/sql/operators.py  Wed Dec 24 10:37:11 2025 +0100
+++ b/sql/operators.py  Sat Jan 24 09:06:14 2026 +0100
@@ -49,7 +49,8 @@
         if param is None:
             param = Flavor.get().param
         if (isinstance(operand, Expression)
-                and not isinstance(operand, Operator)):
+                and (not isinstance(operand, Operator)
+                    or isinstance(operand, UnaryOperator))):
             return str(operand)
         elif isinstance(operand, (Expression, Select, CombiningQuery)):
             return '(%s)' % operand
@@ -458,7 +459,24 @@
     _operator = 'EXISTS'
 
 
-class Any(UnaryOperator):
+class _ArrayOperator(UnaryOperator):
+    __slots__ = ()
+
+    @property
+    def params(self):
+        if isinstance(self.operand, (list, tuple, array)):
+            return (list(self.operand),)
+        return super().params
+
+    def _format(self, operand, param=None):
+        if param is None:
+            param = Flavor.get().param
+        if isinstance(operand, (list, tuple, array)):
+            return '(%s)' % param
+        return super()._format(operand, param=param)
+
+
+class Any(_ArrayOperator):
     __slots__ = ()
     _operator = 'ANY'
 
@@ -466,7 +484,7 @@
 Some = Any
 
 
-class All(UnaryOperator):
+class All(_ArrayOperator):
     __slots__ = ()
     _operator = 'ALL'
 
diff -r 05c585f43f17 -r c42894817c35 sql/tests/test_operators.py
--- a/sql/tests/test_operators.py       Wed Dec 24 10:37:11 2025 +0100
+++ b/sql/tests/test_operators.py       Sat Jan 24 09:06:14 2026 +0100
@@ -6,10 +6,10 @@
 
 from sql import Flavor, Literal, Null, Table
 from sql.operators import (
-    Abs, And, Between, Div, Equal, Exists, FloorDiv, Greater, GreaterEqual,
-    ILike, In, Is, IsDistinct, IsNot, IsNotDistinct, Less, LessEqual, Like,
-    LShift, Mod, Mul, Neg, Not, NotBetween, NotEqual, NotILike, NotIn, NotLike,
-    Operator, Or, Pos, Pow, RShift, Sub)
+    Abs, And, Any, Between, Div, Equal, Exists, FloorDiv, Greater,
+    GreaterEqual, ILike, In, Is, IsDistinct, IsNot, IsNotDistinct, Less,
+    LessEqual, Like, LShift, Mod, Mul, Neg, Not, NotBetween, NotEqual,
+    NotILike, NotIn, NotLike, Operator, Or, Pos, Pow, RShift, Sub)
 
 
 class TestOperators(unittest.TestCase):
@@ -418,3 +418,21 @@
                 self.assertIn(
                     'FloorDiv operator is deprecated, use Div function',
                     str(w[-1].message))
+
+    def test_any(self):
+        any_ = Any(self.table.select(self.table.c1, where=self.table.c2 == 1))
+        self.assertEqual(str(any_),
+            'ANY (SELECT "a"."c1" FROM "t" AS "a" WHERE "a"."c2" = %s)')
+        self.assertEqual(any_.params, (1,))
+
+        for value in [[1, 2, 3], (1, 2, 3), array('l', [1, 2, 3])]:
+            with self.subTest(value=value):
+                any_ = Any(value)
+                self.assertEqual(str(any_), 'ANY (%s)')
+                self.assertEqual(any_.params, ([1, 2, 3],))
+
+    def test_binary_unary(self):
+        operator = Equal(self.table.c1, Any([1, 2, 3]))
+
+        self.assertEqual(str(operator), '"c1" = ANY (%s)')
+        self.assertEqual(operator.params, ([1, 2, 3],))

Reply via email to