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
+
+- 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: info@b2ck.com
+Author: Tryton
+Author-email: python-sql@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: info@b2ck.com
+Author: Tryton
+Author-email: python-sql@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='info@b2ck.com',
+ author='Tryton',
+ author_email='python-sql@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 =
+