Hello community,

here is the log from the commit of package python-python-sql for 
openSUSE:Factory checked in at 2018-10-04 19:01:31
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-sql (Old)
 and      /work/SRC/openSUSE:Factory/.python-python-sql.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-python-sql"

Thu Oct  4 19:01:31 2018 rev:3 rq:639727 version:1.0.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-python-sql/python-python-sql.changes      
2017-04-26 21:43:44.207555897 +0200
+++ /work/SRC/openSUSE:Factory/.python-python-sql.new/python-python-sql.changes 
2018-10-04 19:01:37.535195100 +0200
@@ -1,0 +2,10 @@
+Mon Oct  1 08:59:59 UTC 2018 - Axel Braun <axel.br...@gmx.de>
+
+- Version 1.0.0
+  * Add Flavor filter_ to fallback to case expression
+  * Allow to use expression in AtTimeZone
+  * Add comparison predicates
+  * Add COLLATE
+  * various bugfixes
+
+-------------------------------------------------------------------

Old:
----
  python-sql-0.9.tar.gz

New:
----
  python-sql-1.0.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-python-sql.spec ++++++
--- /var/tmp/diff_new_pack.lf78Tx/_old  2018-10-04 19:01:38.191194412 +0200
+++ /var/tmp/diff_new_pack.lf78Tx/_new  2018-10-04 19:01:38.195194408 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-python-sql
 #
-# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -12,29 +12,23 @@
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
+# Please submit bugfixes or comments via https://bugs.opensuse.org/
 #
 
-%{?!python_module:%define python_module() python-%{**} python3-%{**}}
 
+%{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define base_name python-sql
 Name:           python-%{base_name}
-BuildRequires:  %{python_module devel}
-BuildRequires:  %{python_module setuptools}
-BuildRequires:  python-rpm-macros
-Version:        0.9
+Version:        1.0.0
 Release:        0
-Source:         
https://pypi.io/packages/source/p/%{base_name}/%{base_name}-%{version}.tar.gz
-Url:            https://pypi.io/project/python-sql
-%if 0%{?suse_version} && 0%{?suse_version} <= 1110
-%{!?python_sitelib: %global python_sitelib %(python -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib()")}
-%else
-BuildArch:      noarch
-%endif   
 Summary:        Library to write SQL queries
 License:        BSD-3-Clause
 Group:          Development/Languages/Python
-BuildRoot:      %{_tmppath}/%{name}-%{version}-build
+URL:            https://pypi.io/project/python-sql
+Source:         
https://pypi.io/packages/source/p/%{base_name}/%{base_name}-%{version}.tar.gz
+BuildRequires:  %{python_module setuptools}
+BuildRequires:  python-rpm-macros
+BuildArch:      noarch
 %python_subpackages
 
 %description
@@ -49,8 +43,10 @@
 %install
 %python_install
 
+%check
+%python_exec setup.py test
+
 %files %{python_files}
-%defattr(-,root,root)
 %doc README
 %{python_sitelib}/*
 

++++++ python-sql-0.9.tar.gz -> python-sql-1.0.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/.drone.yml 
new/python-sql-1.0.0/.drone.yml
--- old/python-sql-0.9/.drone.yml       1970-01-01 01:00:00.000000000 +0100
+++ new/python-sql-1.0.0/.drone.yml     2018-08-18 13:25:33.000000000 +0200
@@ -0,0 +1,29 @@
+clone:
+    hg:
+        image: plugins/hg
+
+pipeline:
+    tox:
+        image: ${IMAGE}
+        commands:
+            - pip install tox
+            - tox -e "${TOXENV}"
+        volumes:
+             - cache:/root/.cache
+
+matrix:
+    include:
+        - IMAGE: python:2.7
+          TOXENV: py27
+        - IMAGE: python:3.4
+          TOXENV: py34
+        - IMAGE: python:3.5
+          TOXENV: py35
+        - IMAGE: python:3.6
+          TOXENV: py36
+        - IMAGE: python:3.7
+          TOXENV: py37
+        - IMAGE: pypy:2
+          TOXENV: pypy
+        - IMAGE: pypy:3
+          TOXENV: pypy3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/.hgtags new/python-sql-1.0.0/.hgtags
--- old/python-sql-0.9/.hgtags  1970-01-01 01:00:00.000000000 +0100
+++ new/python-sql-1.0.0/.hgtags        2018-09-30 14:27:13.000000000 +0200
@@ -0,0 +1,10 @@
+2543c23b577ce86260954129b1fb002628ffa80c 0.1
+e8322fa5b1148647989c52214f07c55a8ce457bb 0.2
+00e4b65c6a964f1fb1d42c9f5e65fe0deda0de57 0.3
+45063df05605c7ca496de0c5f4adddc0b135bd4f 0.4
+aca607de1cf8b18388aac9e5aa11da9e922ab976 0.5
+43793e923bac2c7c293340dfa595736ecf6d92a4 0.6
+cc2ba29c02bc0647f1feb3ff2f49df27af3dd9d6 0.7
+5ef77ab47a7bdaaf568ae1c5b3f1b0698ee2418c 0.8
+e3bdeb99dd975024e30d8af18c324a0a7f860e63 0.9
+7459778aa23150aa6ac39356621c29d368ae1f36 1.0.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/CHANGELOG 
new/python-sql-1.0.0/CHANGELOG
--- old/python-sql-0.9/CHANGELOG        2017-04-24 12:17:09.000000000 +0200
+++ new/python-sql-1.0.0/CHANGELOG      2018-09-30 14:26:46.000000000 +0200
@@ -1,3 +1,10 @@
+Version 1.0.0 - 2018-09-30
+* Add Flavor filter_ to fallback to case expression
+* Allow to use expression in AtTimeZone
+* Fix Select query in returning
+* Add comparison predicates
+* Add COLLATE
+
 Version 0.9 - 2017-04-24
 * Add distinct_on on Select
 * Allow to use Select as Column of Select query
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/PKG-INFO new/python-sql-1.0.0/PKG-INFO
--- old/python-sql-0.9/PKG-INFO 2017-04-24 12:19:14.000000000 +0200
+++ new/python-sql-1.0.0/PKG-INFO       2018-09-30 14:27:57.000000000 +0200
@@ -1,11 +1,12 @@
 Metadata-Version: 1.1
 Name: python-sql
-Version: 0.9
+Version: 1.0.0
 Summary: Library to write SQL queries
 Home-page: http://python-sql.tryton.org/
-Author: B2CK
-Author-email: i...@b2ck.com
+Author: Tryton
+Author-email: python-...@tryton.org
 License: BSD
+Description-Content-Type: UNKNOWN
 Description: python-sql
         ==========
         
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/python_sql.egg-info/PKG-INFO 
new/python-sql-1.0.0/python_sql.egg-info/PKG-INFO
--- old/python-sql-0.9/python_sql.egg-info/PKG-INFO     2017-04-24 
12:19:12.000000000 +0200
+++ new/python-sql-1.0.0/python_sql.egg-info/PKG-INFO   2018-09-30 
14:27:57.000000000 +0200
@@ -1,11 +1,12 @@
 Metadata-Version: 1.1
 Name: python-sql
-Version: 0.9
+Version: 1.0.0
 Summary: Library to write SQL queries
 Home-page: http://python-sql.tryton.org/
-Author: B2CK
-Author-email: i...@b2ck.com
+Author: Tryton
+Author-email: python-...@tryton.org
 License: BSD
+Description-Content-Type: UNKNOWN
 Description: python-sql
         ==========
         
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/python_sql.egg-info/SOURCES.txt 
new/python-sql-1.0.0/python_sql.egg-info/SOURCES.txt
--- old/python-sql-0.9/python_sql.egg-info/SOURCES.txt  2017-04-24 
12:19:12.000000000 +0200
+++ new/python-sql-1.0.0/python_sql.egg-info/SOURCES.txt        2018-09-30 
14:27:57.000000000 +0200
@@ -1,7 +1,10 @@
+.drone.yml
+.hgtags
 CHANGELOG
 MANIFEST.in
 README
 setup.py
+tox.ini
 python_sql.egg-info/PKG-INFO
 python_sql.egg-info/SOURCES.txt
 python_sql.egg-info/dependency_links.txt
@@ -16,6 +19,7 @@
 sql/tests/test_alias.py
 sql/tests/test_as.py
 sql/tests/test_cast.py
+sql/tests/test_collate.py
 sql/tests/test_column.py
 sql/tests/test_combining_query.py
 sql/tests/test_conditionals.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/setup.cfg 
new/python-sql-1.0.0/setup.cfg
--- old/python-sql-0.9/setup.cfg        2017-04-24 12:19:14.000000000 +0200
+++ new/python-sql-1.0.0/setup.cfg      2018-09-30 14:27:57.000000000 +0200
@@ -1,5 +1,4 @@
 [egg_info]
 tag_build = 
 tag_date = 0
-tag_svn_revision = 0
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/setup.py new/python-sql-1.0.0/setup.py
--- old/python-sql-0.9/setup.py 2015-06-22 17:46:50.000000000 +0200
+++ new/python-sql-1.0.0/setup.py       2018-09-30 14:17:37.000000000 +0200
@@ -42,12 +42,13 @@
     init = read(os.path.join('sql', '__init__.py'))
     return re.search("__version__ = '([0-9.]*)'", init).group(1)
 
+
 setup(name='python-sql',
     version=get_version(),
     description='Library to write SQL queries',
     long_description=read('README'),
-    author='B2CK',
-    author_email='i...@b2ck.com',
+    author='Tryton',
+    author_email='python-...@tryton.org',
     url='http://python-sql.tryton.org/',
     packages=find_packages(),
     classifiers=[
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/__init__.py 
new/python-sql-1.0.0/sql/__init__.py
--- old/python-sql-0.9/sql/__init__.py  2016-09-14 11:25:03.000000000 +0200
+++ new/python-sql-1.0.0/sql/__init__.py        2018-09-30 14:25:27.000000000 
+0200
@@ -1,8 +1,8 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2011-2016, Cédric Krier
+# Copyright (c) 2011-2018, Cédric Krier
 # Copyright (c) 2013-2014, Nicolas Évrard
-# Copyright (c) 2011-2016, B2CK
+# Copyright (c) 2011-2018, B2CK
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -29,16 +29,16 @@
 
 from __future__ import division
 
-__version__ = '0.9'
-__all__ = ['Flavor', 'Table', 'Values', 'Literal', 'Column', 'Join',
-    'Asc', 'Desc', 'NullsFirst', 'NullsLast', 'format2numeric']
-
 import string
 import warnings
 from threading import local, currentThread
 from collections import defaultdict
 from itertools import chain
 
+__version__ = '1.0.0'
+__all__ = ['Flavor', 'Table', 'Values', 'Literal', 'Column', 'Join',
+    'Asc', 'Desc', 'NullsFirst', 'NullsLast', 'format2numeric']
+
 
 def alias(i, letters=string.ascii_lowercase):
     '''
@@ -78,7 +78,7 @@
 
     def __init__(self, limitstyle='limit', max_limit=None, paramstyle='format',
             ilike=False, no_as=False, no_boolean=False, null_ordering=True,
-            function_mapping=None):
+            function_mapping=None, filter_=False):
         assert limitstyle in ['fetch', 'limit', 'rownum']
         self.limitstyle = limitstyle
         self.max_limit = max_limit
@@ -88,6 +88,7 @@
         self.no_boolean = no_boolean
         self.null_ordering = null_ordering
         self.function_mapping = function_mapping or {}
+        self.filter_ = filter_
 
     @property
     def param(self):
@@ -693,10 +694,13 @@
             # TODO manage DEFAULT
         elif self.values is None:
             values = ' DEFAULT VALUES'
-        returning = ''
-        if self.returning:
-            returning = ' RETURNING ' + ', '.join(map(str, self.returning))
         with AliasManager():
+            table = self.table
+            AliasManager.set(table, str(table)[1:-1])
+            returning = ''
+            if self.returning:
+                returning = ' RETURNING ' + ', '.join(
+                    map(self._format, self.returning))
             return (self._with_str()
                 + 'INSERT INTO %s' % self.table + columns
                 + values + returning)
@@ -766,7 +770,8 @@
                 where = ' WHERE ' + str(self.where)
             returning = ''
             if self.returning:
-                returning = ' RETURNING ' + ', '.join(map(str, self.returning))
+                returning = ' RETURNING ' + ', '.join(
+                    map(self._format, self.returning))
             return (self._with_str()
                 + 'UPDATE %s SET ' % table + values + from_
                 + where + returning)
@@ -1218,6 +1223,9 @@
     def cast(self, typename):
         return Cast(self, typename)
 
+    def collate(self, collation):
+        return Collate(self, collation)
+
     @property
     def asc(self):
         return Asc(self)
@@ -1262,6 +1270,7 @@
                 return ()
         return (self._value,)
 
+
 Null = None
 
 
@@ -1273,6 +1282,8 @@
     @property
     def params(self):
         return ()
+
+
 _rownum = _Rownum()
 
 
@@ -1329,7 +1340,7 @@
     __slots__ = ('expression', 'typename')
 
     def __init__(self, expression, typename):
-        super(Expression, self).__init__()
+        super(Cast, self).__init__()
         self.expression = expression
         self.typename = typename
 
@@ -1342,6 +1353,31 @@
 
     @property
     def params(self):
+        if isinstance(self.expression, Expression):
+            return self.expression.params
+        else:
+            return (self.expression,)
+
+
+class Collate(Expression):
+    __slots__ = ('expression', 'collation')
+
+    def __init__(self, expression, collation):
+        super(Collate, self).__init__()
+        self.expression = expression
+        self.collation = collation
+
+    def __str__(self):
+        if isinstance(self.expression, Expression):
+            value = self.expression
+        else:
+            value = Flavor.get().param
+        if '"' in self.collation:
+            raise ValueError("Wrong collation %s" % self.collation)
+        return '%s COLLATE "%s"' % (value, self.collation)
+
+    @property
+    def params(self):
         if isinstance(self.expression, Expression):
             return self.expression.params
         else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/aggregate.py 
new/python-sql-1.0.0/sql/aggregate.py
--- old/python-sql-0.9/sql/aggregate.py 2015-09-08 17:51:02.000000000 +0200
+++ new/python-sql-1.0.0/sql/aggregate.py       2018-08-18 13:25:33.000000000 
+0200
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2011-2015, Cédric Krier
-# Copyright (c) 2011-2015, B2CK
+# Copyright (c) 2011-2018, Cédric Krier
+# Copyright (c) 2011-2018, B2CK
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -27,7 +27,7 @@
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
-from sql import Expression, Window
+from sql import Expression, Window, Flavor, Literal
 
 __all__ = ['Avg', 'BitAnd', 'BitOr', 'BoolAnd', 'BoolOr', 'Count', 'Every',
     'Max', 'Min', 'Stddev', 'Sum', 'Variance']
@@ -89,15 +89,24 @@
             assert isinstance(value, Window)
         self._window = value
 
+    @property
+    def _case_expression(self):
+        return self.expression
+
     def __str__(self):
         quantifier = 'DISTINCT ' if self.distinct else ''
-        aggregate = '%s(%s%s)' % (self._sql, quantifier, self.expression)
+        has_filter = Flavor.get().filter_
+        expression = self.expression
+        if self.filter_ and not has_filter:
+            from sql.conditionals import Case
+            expression = Case((self.filter_, self._case_expression))
+        aggregate = '%s(%s%s)' % (self._sql, quantifier, expression)
         within = ''
         if self.within:
             within = (' WITHIN GROUP (ORDER BY %s)'
                 % ', '.join(map(str, self.within)))
         filter_ = ''
-        if self.filter_:
+        if self.filter_ and has_filter:
             filter_ = ' FILTER (WHERE %s)' % self.filter_
         window = ''
         if self.window:
@@ -106,11 +115,17 @@
 
     @property
     def params(self):
-        p = list(self.expression.params)
+        has_filter = Flavor.get().filter_
+        p = []
+        if self.filter_ and not has_filter:
+            p.extend(self.filter_.params)
+            p.extend(self._case_expression.params)
+        else:
+            p.extend(self.expression.params)
         if self.within:
             for expression in self.within:
                 p.extend(expression.params)
-        if self.filter_:
+        if self.filter_ and has_filter:
             p.extend(self.filter_.params)
         return tuple(p)
 
@@ -144,6 +159,14 @@
     __slots__ = ()
     _sql = 'COUNT'
 
+    @property
+    def _case_expression(self):
+        expression = super(Count, self)._case_expression
+        if (isinstance(self.expression, Literal)
+                and expression.value == '*'):
+            expression = Literal(1)
+        return expression
+
 
 class Every(Aggregate):
     __slots__ = ()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/functions.py 
new/python-sql-1.0.0/sql/functions.py
--- old/python-sql-0.9/sql/functions.py 2015-07-01 19:44:40.000000000 +0200
+++ new/python-sql-1.0.0/sql/functions.py       2018-08-18 13:25:33.000000000 
+0200
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2011-2015, Cédric Krier
-# Copyright (c) 2011-2015, B2CK
+# Copyright (c) 2011-2018, Cédric Krier
+# Copyright (c) 2011-2018, B2CK
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -472,15 +472,21 @@
         Mapping = flavor.function_mapping.get(self.__class__)
         if Mapping:
             return str(Mapping(self.field, self.zone))
-        param = flavor.param
-        return '%s AT TIME ZONE %s' % (str(self.field), param)
+        if isinstance(self.zone, Expression):
+            zone = str(self.zone)
+        else:
+            zone = flavor.param
+        return '%s AT TIME ZONE %s' % (str(self.field), zone)
 
     @property
     def params(self):
         Mapping = Flavor.get().function_mapping.get(self.__class__)
         if Mapping:
             return Mapping(self.field, self.zone).params
-        return self.field.params + (self.zone,)
+        if isinstance(self.zone, Expression):
+            return self.field.params + self.zone.params
+        else:
+            return self.field.params + (self.zone,)
 
 
 class WindowFunction(Function):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/operators.py 
new/python-sql-1.0.0/sql/operators.py
--- old/python-sql-0.9/sql/operators.py 2015-06-22 12:48:33.000000000 +0200
+++ new/python-sql-1.0.0/sql/operators.py       2018-09-30 14:17:53.000000000 
+0200
@@ -31,9 +31,10 @@
 from sql import Expression, Select, CombiningQuery, Flavor, Null
 
 __all__ = ['And', 'Or', 'Not', 'Less', 'Greater', 'LessEqual', 'GreaterEqual',
-    'Equal', 'NotEqual', 'Add', 'Sub', 'Mul', 'Div', 'FloorDiv', 'Mod', 'Pow',
-    'SquareRoot', 'CubeRoot', 'Factorial', 'Abs', 'BAnd', 'BOr', 'BXor',
-    'BNot', 'LShift', 'RShift', 'Concat', 'Like', 'NotLike', 'ILike',
+    'Equal', 'NotEqual', 'Between', 'NotBetween', 'IsDistinct',
+    'IsNotDistinct', 'Is', 'IsNot', 'Add', 'Sub', 'Mul', 'Div', 'FloorDiv',
+    'Mod', 'Pow', 'SquareRoot', 'CubeRoot', 'Factorial', 'Abs', 'BAnd', 'BOr',
+    'BXor', 'BNot', 'LShift', 'RShift', 'Concat', 'Like', 'NotLike', 'ILike',
     'NotILike', 'In', 'NotIn', 'Exists', 'Any', 'Some', 'All']
 
 
@@ -225,6 +226,75 @@
         return super(Equal, self).__str__()
 
 
+class Between(Operator):
+    __slots__ = ('operand', 'left', 'right', 'symmetric')
+    _operator = 'BETWEEN'
+
+    def __init__(self, operand, left, right, symmetric=False):
+        self.operand = operand
+        self.left = left
+        self.right = right
+        self.symmetric = symmetric
+
+    @property
+    def _operands(self):
+        return (self.operand, self.left, self.right)
+
+    def __str__(self):
+        operator = self._operator
+        if self.symmetric:
+            operator += ' SYMMETRIC'
+        return '(%s %s %s AND %s)' % (
+            self._format(self.operand), operator,
+            self._format(self.left), self._format(self.right))
+
+    def __invert__(self):
+        return _INVERT[self.__class__](
+            self.operand, self.left, self.right, self.symmetric)
+
+
+class NotBetween(Between):
+    __slots__ = ()
+    _operator = 'NOT BETWEEN'
+
+
+class IsDistinct(BinaryOperator):
+    __slots__ = ()
+    _operator = 'IS DISTINCT FROM'
+
+
+class IsNotDistinct(IsDistinct):
+    __slots__ = ()
+    _operator = 'IS NOT DISTINCT FROM'
+
+
+class Is(BinaryOperator):
+    __slots__ = ()
+    _operator = 'IS'
+
+    def __init__(self, left, right):
+        assert right in [None, True, False]
+        super(Is, self).__init__(left, right)
+
+    @property
+    def _operands(self):
+        return (self.left,)
+
+    def __str__(self):
+        if self.right is None:
+            return '(%s %s UNKNOWN)' % (
+                self._format(self.left), self._operator)
+        elif self.right is True:
+            return '(%s %s TRUE)' % (self._format(self.left), self._operator)
+        elif self.right is False:
+            return '(%s %s FALSE)' % (self._format(self.left), self._operator)
+
+
+class IsNot(Is):
+    __slots__ = ()
+    _operator = 'IS NOT'
+
+
 class Add(BinaryOperator):
     __slots__ = ()
     _operator = '+'
@@ -389,6 +459,7 @@
     __slots__ = ()
     _operator = 'ANY'
 
+
 Some = Any
 
 
@@ -404,6 +475,12 @@
     GreaterEqual: Less,
     Equal: NotEqual,
     NotEqual: Equal,
+    Between: NotBetween,
+    NotBetween: Between,
+    IsDistinct: IsNotDistinct,
+    IsNotDistinct: IsDistinct,
+    Is: IsNot,
+    IsNot: Is,
     Like: NotLike,
     NotLike: Like,
     ILike: NotILike,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/tests/__init__.py 
new/python-sql-1.0.0/sql/tests/__init__.py
--- old/python-sql-0.9/sql/tests/__init__.py    2015-03-05 09:36:24.000000000 
+0100
+++ new/python-sql-1.0.0/sql/tests/__init__.py  2018-09-30 14:18:53.000000000 
+0200
@@ -64,6 +64,7 @@
     runner = unittest.TextTestRunner()
     return runner.run(suite)
 
+
 if __name__ == '__main__':
     sys.path.insert(0, os.path.dirname(os.path.dirname(
                 os.path.dirname(os.path.abspath(__file__)))))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/tests/test_aggregate.py 
new/python-sql-1.0.0/sql/tests/test_aggregate.py
--- old/python-sql-0.9/sql/tests/test_aggregate.py      2015-09-08 
17:51:02.000000000 +0200
+++ new/python-sql-1.0.0/sql/tests/test_aggregate.py    2018-08-18 
13:25:33.000000000 +0200
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2011-2015, Cédric Krier
-# Copyright (c) 2011-2015, B2CK
+# Copyright (c) 2011-2018, Cédric Krier
+# Copyright (c) 2011-2018, B2CK
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -28,8 +28,8 @@
 
 import unittest
 
-from sql import Table, Window, AliasManager
-from sql.aggregate import Avg
+from sql import Table, Window, AliasManager, Flavor, Literal
+from sql.aggregate import Avg, Count
 
 
 class TestAggregate(unittest.TestCase):
@@ -48,9 +48,27 @@
         self.assertEqual(avg.params, ())
 
     def test_filter(self):
-        avg = Avg(self.table.a, filter_=self.table.a > 0)
-        self.assertEqual(str(avg), 'AVG("a") FILTER (WHERE ("a" > %s))')
-        self.assertEqual(avg.params, (0,))
+        flavor = Flavor(filter_=True)
+        Flavor.set(flavor)
+        try:
+            avg = Avg(self.table.a + 1, filter_=self.table.a > 0)
+            self.assertEqual(
+                str(avg), 'AVG(("a" + %s)) FILTER (WHERE ("a" > %s))')
+            self.assertEqual(avg.params, (1, 0))
+        finally:
+            Flavor.set(Flavor())
+
+    def test_filter_case(self):
+        avg = Avg(self.table.a + 1, filter_=self.table.a > 0)
+        self.assertEqual(
+            str(avg), 'AVG(CASE WHEN ("a" > %s) THEN ("a" + %s) END)')
+        self.assertEqual(avg.params, (0, 1))
+
+    def test_filter_case_count_star(self):
+        count = Count(Literal('*'), filter_=self.table.a > 0)
+        self.assertEqual(
+            str(count), 'COUNT(CASE WHEN ("a" > %s) THEN %s END)')
+        self.assertEqual(count.params, (0, 1))
 
     def test_window(self):
         avg = Avg(self.table.c, window=Window([]))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/tests/test_collate.py 
new/python-sql-1.0.0/sql/tests/test_collate.py
--- old/python-sql-0.9/sql/tests/test_collate.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/python-sql-1.0.0/sql/tests/test_collate.py      2018-08-18 
13:25:33.000000000 +0200
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017, Cédric Krier
+# Copyright (c) 2017, B2CK
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the <organization> nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+
+from sql import Collate, Column, Table
+
+
+class TestCollate(unittest.TestCase):
+    column = Column(Table('t'), 'c')
+
+    def test_collate(self):
+        for collate in [Collate(self.column, 'C'), self.column.collate('C')]:
+            self.assertEqual(str(collate), '"c" COLLATE "C"')
+            self.assertEqual(collate.params, ())
+
+    def test_collate_no_expression(self):
+        collate = Collate("foo", 'C')
+        self.assertEqual(str(collate), '%s COLLATE "C"')
+        self.assertEqual(collate.params, ("foo",))
+
+    def test_collate_injection(self):
+        collate = Collate(self.column, 'C";')
+        with self.assertRaises(ValueError):
+            str(collate)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/tests/test_delete.py 
new/python-sql-1.0.0/sql/tests/test_delete.py
--- old/python-sql-0.9/sql/tests/test_delete.py 2015-02-01 23:31:58.000000000 
+0100
+++ new/python-sql-1.0.0/sql/tests/test_delete.py       2018-09-30 
14:18:37.000000000 +0200
@@ -66,5 +66,6 @@
             where=self.table.c2.in_(w.select(w.c3)))
         self.assertEqual(str(query),
             'WITH "a" AS (SELECT "b"."c1" FROM "t1" AS "b") '
-            'DELETE FROM "t" WHERE ("c2" IN (SELECT "a"."c3" FROM "a" AS 
"a"))')
+            'DELETE FROM "t" WHERE '
+            '("c2" IN (SELECT "a"."c3" FROM "a" AS "a"))')
         self.assertEqual(query.params, ())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/tests/test_functions.py 
new/python-sql-1.0.0/sql/tests/test_functions.py
--- old/python-sql-0.9/sql/tests/test_functions.py      2015-03-18 
14:35:55.000000000 +0100
+++ new/python-sql-1.0.0/sql/tests/test_functions.py    2018-08-18 
13:25:33.000000000 +0200
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2011-2015, Cédric Krier
-# Copyright (c) 2011-2015, B2CK
+# Copyright (c) 2011-2018, Cédric Krier
+# Copyright (c) 2011-2018, B2CK
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -110,6 +110,11 @@
         self.assertEqual(str(time_zone), '"c1" AT TIME ZONE %s')
         self.assertEqual(time_zone.params, ('UTC',))
 
+    def test_at_time_zone_expression(self):
+        time_zone = AtTimeZone(self.table.c1, self.table.zone)
+        self.assertEqual(str(time_zone), '"c1" AT TIME ZONE "zone"')
+        self.assertEqual(time_zone.params, ())
+
     def test_at_time_zone_mapping(self):
         class MyAtTimeZone(Function):
             _function = 'MY_TIMEZONE'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/tests/test_insert.py 
new/python-sql-1.0.0/sql/tests/test_insert.py
--- old/python-sql-0.9/sql/tests/test_insert.py 2015-02-01 23:31:07.000000000 
+0100
+++ new/python-sql-1.0.0/sql/tests/test_insert.py       2018-08-18 
13:25:33.000000000 +0200
@@ -76,7 +76,19 @@
             [['foo', 'bar']], returning=[self.table.c1, self.table.c2])
         self.assertEqual(str(query),
             'INSERT INTO "t" ("c1", "c2") VALUES (%s, %s) '
-            'RETURNING "c1", "c2"')
+            'RETURNING "t"."c1", "t"."c2"')
+        self.assertEqual(query.params, ('foo', 'bar'))
+
+    def test_insert_returning_select(self):
+        t1 = Table('t1')
+        t2 = Table('t2')
+        query = t1.insert([t1.c], [['foo']],
+            returning=[
+                t2.select(t2.c, where=(t2.c1 == t1.c) & (t2.c2 == 'bar'))])
+        self.assertEqual(str(query),
+            'INSERT INTO "t1" ("c") VALUES (%s) '
+            'RETURNING (SELECT "b"."c" FROM "t2" AS "b" '
+            'WHERE (("b"."c1" = "t1"."c") AND ("b"."c2" = %s)))')
         self.assertEqual(query.params, ('foo', 'bar'))
 
     def test_with(self):
@@ -88,6 +100,14 @@
             with_=[w],
             values=w.select())
         self.assertEqual(str(query),
-            'WITH "a" AS (SELECT * FROM "t1" AS "b") '
+            'WITH "b" AS (SELECT * FROM "t1" AS "c") '
             'INSERT INTO "t" ("c1") SELECT * FROM "a" AS "a"')
         self.assertEqual(query.params, ())
+
+    def test_schema(self):
+        t1 = Table('t1', 'default')
+        query = t1.insert([t1.c1], [['foo']])
+
+        self.assertEqual(str(query),
+            'INSERT INTO "default"."t1" ("c1") VALUES (%s)')
+        self.assertEqual(query.params, ('foo',))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/tests/test_operators.py 
new/python-sql-1.0.0/sql/tests/test_operators.py
--- old/python-sql-0.9/sql/tests/test_operators.py      2015-06-22 
12:49:51.000000000 +0200
+++ new/python-sql-1.0.0/sql/tests/test_operators.py    2018-08-18 
13:25:33.000000000 +0200
@@ -32,8 +32,9 @@
 
 from sql import Table, Literal, Null, Flavor
 from sql.operators import (And, Or, Not, Neg, Pos, Less, Greater, LessEqual,
-    GreaterEqual, Equal, NotEqual, Sub, Mul, Div, Mod, Pow, Abs, LShift,
-    RShift, Like, NotLike, ILike, NotILike, In, NotIn, FloorDiv, Exists)
+    GreaterEqual, Equal, NotEqual, Between, NotBetween, IsDistinct,
+    IsNotDistinct, Is, IsNot, Sub, Mul, Div, Mod, Pow, Abs, LShift, RShift,
+    Like, NotLike, ILike, NotILike, In, NotIn, FloorDiv, Exists)
 
 
 class TestOperators(unittest.TestCase):
@@ -172,6 +173,74 @@
         self.assertEqual(str(equal), '("c1" IS NOT NULL)')
         self.assertEqual(equal.params, ())
 
+    def test_between(self):
+        for between in [Between(self.table.c1, 1, 2),
+                ~NotBetween(self.table.c1, 1, 2)]:
+            self.assertEqual(str(between), '("c1" BETWEEN %s AND %s)')
+            self.assertEqual(between.params, (1, 2))
+
+        between = Between(
+            self.table.c1, self.table.c2, self.table.c3, symmetric=True)
+        self.assertEqual(
+            str(between), '("c1" BETWEEN SYMMETRIC "c2" AND "c3")')
+        self.assertEqual(between.params, ())
+
+    def test_not_between(self):
+        for between in [NotBetween(self.table.c1, 1, 2),
+                ~Between(self.table.c1, 1, 2)]:
+            self.assertEqual(str(between), '("c1" NOT BETWEEN %s AND %s)')
+            self.assertEqual(between.params, (1, 2))
+
+        between = NotBetween(
+            self.table.c1, self.table.c2, self.table.c3, symmetric=True)
+        self.assertEqual(
+            str(between), '("c1" NOT BETWEEN SYMMETRIC "c2" AND "c3")')
+        self.assertEqual(between.params, ())
+
+    def test_is_distinct(self):
+        for distinct in [IsDistinct(self.table.c1, self.table.c2),
+                ~IsNotDistinct(self.table.c1, self.table.c2)]:
+            self.assertEqual(str(distinct), '("c1" IS DISTINCT FROM "c2")')
+            self.assertEqual(distinct.params, ())
+
+    def test_is_not_distinct(self):
+        for distinct in [IsNotDistinct(self.table.c1, self.table.c2),
+                ~IsDistinct(self.table.c1, self.table.c2)]:
+            self.assertEqual(str(distinct), '("c1" IS NOT DISTINCT FROM "c2")')
+            self.assertEqual(distinct.params, ())
+
+    def test_is(self):
+        for is_ in [Is(self.table.c1, None),
+                ~IsNot(self.table.c1, None)]:
+            self.assertEqual(str(is_), '("c1" IS UNKNOWN)')
+            self.assertEqual(is_.params, ())
+
+        for is_ in [Is(self.table.c1, True),
+                ~IsNot(self.table.c1, True)]:
+            self.assertEqual(str(is_), '("c1" IS TRUE)')
+            self.assertEqual(is_.params, ())
+
+        for is_ in [Is(self.table.c1, False),
+                ~IsNot(self.table.c1, False)]:
+            self.assertEqual(str(is_), '("c1" IS FALSE)')
+            self.assertEqual(is_.params, ())
+
+    def test_is_not(self):
+        for is_ in [IsNot(self.table.c1, None),
+                ~Is(self.table.c1, None)]:
+            self.assertEqual(str(is_), '("c1" IS NOT UNKNOWN)')
+            self.assertEqual(is_.params, ())
+
+        for is_ in [IsNot(self.table.c1, True),
+                ~Is(self.table.c1, True)]:
+            self.assertEqual(str(is_), '("c1" IS NOT TRUE)')
+            self.assertEqual(is_.params, ())
+
+        for is_ in [IsNot(self.table.c1, False),
+                ~Is(self.table.c1, False)]:
+            self.assertEqual(str(is_), '("c1" IS NOT FALSE)')
+            self.assertEqual(is_.params, ())
+
     def test_sub(self):
         for sub in [Sub(self.table.c1, self.table.c2),
                 self.table.c1 - self.table.c2]:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/sql/tests/test_update.py 
new/python-sql-1.0.0/sql/tests/test_update.py
--- old/python-sql-0.9/sql/tests/test_update.py 2015-02-01 23:31:27.000000000 
+0100
+++ new/python-sql-1.0.0/sql/tests/test_update.py       2018-08-18 
13:25:33.000000000 +0200
@@ -71,6 +71,18 @@
             'UPDATE "t" SET "c" = %s RETURNING "t"."c"')
         self.assertEqual(query.params, ('foo',))
 
+    def test_update_returning_select(self):
+        t1 = Table('t1')
+        t2 = Table('t2')
+        query = t1.update([t1.c], ['foo'],
+            returning=[
+                t2.select(t2.c, where=(t2.c1 == t1.c) & (t2.c2 == 'bar'))])
+        self.assertEqual(str(query),
+            'UPDATE "t1" SET "c" = %s '
+            'RETURNING (SELECT "b"."c" FROM "t2" AS "b" '
+            'WHERE (("b"."c1" = "t1"."c") AND ("b"."c2" = %s)))')
+        self.assertEqual(query.params, ('foo', 'bar'))
+
     def test_with(self):
         t1 = Table('t1')
         w = With(query=t1.select(t1.c1))
@@ -84,3 +96,21 @@
             'UPDATE "t" SET "c2" = (SELECT "b"."c3" FROM "b" AS "b" '
             'WHERE ("b"."c4" = %s))')
         self.assertEqual(query.params, (2,))
+
+    def test_schema(self):
+        t1 = Table('t1', 'default')
+        query = t1.update([t1.c1], ['foo'])
+
+        self.assertEqual(str(query), 'UPDATE "default"."t1" SET "c1" = %s')
+        self.assertEqual(query.params, ('foo',))
+
+    def test_schema_subselect(self):
+        t1 = Table('t1', 'default')
+        t2 = Table('t2', 'default')
+        query = t1.update([t1.c1], t2.select(t2.c, where=t2.i == t1.i))
+
+        self.assertEqual(str(query),
+            'UPDATE "default"."t1" SET "c1" = ('
+            'SELECT "b"."c" FROM "default"."t2" AS "b" '
+            'WHERE ("b"."i" = "default"."t1"."i"))')
+        self.assertEqual(query.params, ())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-sql-0.9/tox.ini new/python-sql-1.0.0/tox.ini
--- old/python-sql-0.9/tox.ini  1970-01-01 01:00:00.000000000 +0100
+++ new/python-sql-1.0.0/tox.ini        2018-08-18 13:25:33.000000000 +0200
@@ -0,0 +1,12 @@
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py26, py27, py33, py34, py35, py36, py37, pypy, pypy3, jython
+
+[testenv]
+commands = {envpython} setup.py test
+deps =
+


Reply via email to