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],))