python-sql 1.5.1__py3-none-any.whl → 1.5.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sql/aggregate.py CHANGED
@@ -8,7 +8,7 @@ _sentinel = object()
8
8
 
9
9
 
10
10
  class Aggregate(Expression):
11
- __slots__ = ('expression', '_distinct', '_order_by', '_within',
11
+ __slots__ = ('_expression', '_distinct', '_order_by', '_within',
12
12
  '_filter', '_window')
13
13
  _sql = ''
14
14
 
@@ -22,13 +22,24 @@ class Aggregate(Expression):
22
22
  self.filter_ = filter_
23
23
  self.window = window
24
24
 
25
+ @property
26
+ def expression(self):
27
+ return self._expression
28
+
29
+ @expression.setter
30
+ def expression(self, value):
31
+ if not isinstance(value, Expression):
32
+ raise ValueError("invalid expression: %r" % value)
33
+ self._expression = value
34
+
25
35
  @property
26
36
  def distinct(self):
27
37
  return self._distinct
28
38
 
29
39
  @distinct.setter
30
40
  def distinct(self, value):
31
- assert isinstance(value, bool)
41
+ if not isinstance(value, bool):
42
+ raise ValueError("invalid distinct: %r" % value)
32
43
  self._distinct = value
33
44
 
34
45
  @property
@@ -40,7 +51,8 @@ class Aggregate(Expression):
40
51
  if value is not None:
41
52
  if isinstance(value, Expression):
42
53
  value = [value]
43
- assert all(isinstance(col, Expression) for col in value)
54
+ if any(not isinstance(col, Expression) for col in value):
55
+ raise ValueError("invalid order by: %r" % value)
44
56
  self._order_by = value
45
57
 
46
58
  @property
@@ -52,7 +64,8 @@ class Aggregate(Expression):
52
64
  if value is not None:
53
65
  if isinstance(value, Expression):
54
66
  value = [value]
55
- assert all(isinstance(col, Expression) for col in value)
67
+ if any(not isinstance(col, Expression) for col in value):
68
+ raise ValueError("invalid within: %r" % value)
56
69
  self._within = value
57
70
 
58
71
  @property
@@ -63,7 +76,8 @@ class Aggregate(Expression):
63
76
  def filter_(self, value):
64
77
  from sql.operators import And, Or
65
78
  if value is not None:
66
- assert isinstance(value, (Expression, And, Or))
79
+ if not isinstance(value, (Expression, And, Or)):
80
+ raise ValueError("invalid filter: %r" % value)
67
81
  self._filter = value
68
82
 
69
83
  @property
@@ -73,7 +87,8 @@ class Aggregate(Expression):
73
87
  @window.setter
74
88
  def window(self, value):
75
89
  if value:
76
- assert isinstance(value, Window)
90
+ if not isinstance(value, Window):
91
+ raise ValueError("invalid window: %r" % value)
77
92
  self._window = value
78
93
 
79
94
  @property
sql/functions.py CHANGED
@@ -39,7 +39,8 @@ class Function(Expression, FromItem):
39
39
 
40
40
  @columns_definitions.setter
41
41
  def columns_definitions(self, value):
42
- assert isinstance(value, list)
42
+ if not isinstance(value, list):
43
+ raise ValueError("invalid columns definitions: %r" % value)
43
44
  self._columns_definitions = value
44
45
 
45
46
  @staticmethod
@@ -286,7 +287,8 @@ class Trim(Function):
286
287
  _function = 'TRIM'
287
288
 
288
289
  def __init__(self, string, position='BOTH', characters=' '):
289
- assert position.upper() in ('LEADING', 'TRAILING', 'BOTH')
290
+ if position.upper() not in {'LEADING', 'TRAILING', 'BOTH'}:
291
+ raise ValueError("invalid position: %r" % position)
290
292
  self.position = position.upper()
291
293
  self.characters = characters
292
294
  self.string = string
@@ -316,10 +318,7 @@ class Trim(Function):
316
318
  if isinstance(arg, str):
317
319
  p.append(arg)
318
320
  else:
319
- try:
320
- p.extend(arg.params)
321
- except AttributeError:
322
- pass
321
+ p.extend(arg.params)
323
322
  return tuple(p)
324
323
 
325
324
 
@@ -486,7 +485,8 @@ class WindowFunction(Function):
486
485
  def filter_(self, value):
487
486
  from sql.operators import And, Or
488
487
  if value is not None:
489
- assert isinstance(value, (Expression, And, Or))
488
+ if not isinstance(value, (Expression, And, Or)):
489
+ raise ValueError("invalid filter: %r" % value)
490
490
  self._filter = value
491
491
 
492
492
  @property
@@ -496,7 +496,8 @@ class WindowFunction(Function):
496
496
  @window.setter
497
497
  def window(self, value):
498
498
  if value:
499
- assert isinstance(value, Window)
499
+ if not isinstance(value, Window):
500
+ raise ValueError("invalid window: %r" % value)
500
501
  self._window = value
501
502
 
502
503
  def __str__(self):
sql/operators.py CHANGED
@@ -121,7 +121,8 @@ class NaryOperator(list, Operator):
121
121
  return self
122
122
 
123
123
  def __str__(self):
124
- return '(' + (' %s ' % self._operator).join(map(str, self)) + ')'
124
+ return '(' + (' %s ' % self._operator).join(
125
+ map(self._format, self)) + ')'
125
126
 
126
127
 
127
128
  class And(NaryOperator):
@@ -248,7 +249,8 @@ class Is(BinaryOperator):
248
249
  _operator = 'IS'
249
250
 
250
251
  def __init__(self, left, right):
251
- assert right in [None, True, False]
252
+ if right not in {None, True, False}:
253
+ raise ValueError("invalid right: %r" % right)
252
254
  super(Is, self).__init__(left, right)
253
255
 
254
256
  @property
@@ -379,7 +381,8 @@ class Like(BinaryOperator):
379
381
 
380
382
  def __init__(self, left, right, escape=None):
381
383
  super().__init__(left, right)
382
- assert not escape or len(escape) == 1
384
+ if escape and len(escape) != 1:
385
+ raise ValueError("invalid escape: %r" % escape)
383
386
  self.escape = escape
384
387
 
385
388
  @property
@@ -3,12 +3,36 @@
3
3
  import unittest
4
4
 
5
5
  from sql import AliasManager, Flavor, Literal, Table, Window
6
- from sql.aggregate import Avg, Count
6
+ from sql.aggregate import Aggregate, Avg, Count
7
7
 
8
8
 
9
9
  class TestAggregate(unittest.TestCase):
10
10
  table = Table('t')
11
11
 
12
+ def test_invalid_expression(self):
13
+ with self.assertRaises(ValueError):
14
+ Aggregate('foo')
15
+
16
+ def test_invalid_distinct(self):
17
+ with self.assertRaises(ValueError):
18
+ Aggregate(self.table.c, distinct='foo')
19
+
20
+ def test_invalid_order(self):
21
+ with self.assertRaises(ValueError):
22
+ Aggregate(self.table.c, order_by=['foo'])
23
+
24
+ def test_invalid_within(self):
25
+ with self.assertRaises(ValueError):
26
+ Aggregate(self.table.c, within=['foo'])
27
+
28
+ def test_invalid_filter(self):
29
+ with self.assertRaises(ValueError):
30
+ Aggregate(self.table.c, filter_='foo')
31
+
32
+ def test_invalid_window(self):
33
+ with self.assertRaises(ValueError):
34
+ Aggregate(self.table.c, window='foo')
35
+
12
36
  def test_avg(self):
13
37
  avg = Avg(self.table.c)
14
38
  self.assertEqual(str(avg), 'AVG("c")')
sql/tests/test_alias.py CHANGED
@@ -61,3 +61,18 @@ class TestAliasManager(unittest.TestCase):
61
61
  self.finish2.wait()
62
62
  if not self.succeed1.is_set() or not self.succeed2.is_set():
63
63
  self.fail()
64
+
65
+ def test_contains(self):
66
+ with AliasManager():
67
+ AliasManager.get(self.t1)
68
+ self.assertTrue(AliasManager.contains(self.t1))
69
+
70
+ def test_contains_exclude(self):
71
+ with AliasManager(exclude=[self.t1]):
72
+ self.assertEqual(AliasManager.get(self.t1), '')
73
+ self.assertFalse(AliasManager.contains(self.t1))
74
+
75
+ def test_set(self):
76
+ with AliasManager():
77
+ AliasManager.set(self.t1, 'foo')
78
+ self.assertEqual(AliasManager.get(self.t1), 'foo')
sql/tests/test_collate.py CHANGED
@@ -17,8 +17,3 @@ class TestCollate(unittest.TestCase):
17
17
  collate = Collate("foo", 'C')
18
18
  self.assertEqual(str(collate), '%s COLLATE "C"')
19
19
  self.assertEqual(collate.params, ("foo",))
20
-
21
- def test_collate_injection(self):
22
- collate = Collate(self.column, 'C";')
23
- with self.assertRaises(ValueError):
24
- str(collate)
@@ -2,7 +2,7 @@
2
2
  # this repository contains the full copyright notices and license terms.
3
3
  import unittest
4
4
 
5
- from sql import Table, Union, With
5
+ from sql import CombiningQuery, Table, Union, With
6
6
 
7
7
 
8
8
  class TestUnion(unittest.TestCase):
@@ -10,6 +10,10 @@ class TestUnion(unittest.TestCase):
10
10
  q2 = Table('t2').select()
11
11
  q3 = Table('t3').select()
12
12
 
13
+ def test_invalid_queries(self):
14
+ with self.assertRaises(ValueError):
15
+ CombiningQuery('foo', 'bar')
16
+
13
17
  def test_union2(self):
14
18
  query = Union(self.q1, self.q2)
15
19
  self.assertEqual(str(query),
sql/tests/test_delete.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # this repository contains the full copyright notices and license terms.
3
3
  import unittest
4
4
 
5
- from sql import Table, With
5
+ from sql import Delete, Table, With
6
6
 
7
7
 
8
8
  class TestDelete(unittest.TestCase):
@@ -27,11 +27,31 @@ class TestDelete(unittest.TestCase):
27
27
  'SELECT "a"."c" FROM "t2" AS "a"))')
28
28
  self.assertEqual(query.params, ())
29
29
 
30
+ def test_delete_invalid_table(self):
31
+ with self.assertRaises(ValueError):
32
+ Delete('foo')
33
+
34
+ def test_delete_invalid_where(self):
35
+ with self.assertRaises(ValueError):
36
+ self.table.delete(where='foo')
37
+
30
38
  def test_delete_returning(self):
31
39
  query = self.table.delete(returning=[self.table.c])
32
40
  self.assertEqual(str(query), 'DELETE FROM "t" RETURNING "c"')
33
41
  self.assertEqual(query.params, ())
34
42
 
43
+ def test_delet_returning_select(self):
44
+ query = self.table.delete(returning=[self.table.select()])
45
+
46
+ self.assertEqual(
47
+ str(query),
48
+ 'DELETE FROM "t" RETURNING (SELECT * FROM "t")')
49
+ self.assertEqual(query.params, ())
50
+
51
+ def test_delete_invalid_returning(self):
52
+ with self.assertRaises(ValueError):
53
+ self.table.delete(returning='foo')
54
+
35
55
  def test_with(self):
36
56
  t1 = Table('t1')
37
57
  w = With(query=t1.select(t1.c1))
@@ -0,0 +1,15 @@
1
+ # This file is part of python-sql. The COPYRIGHT file at the top level of
2
+ # this repository contains the full copyright notices and license terms.
3
+
4
+ import unittest
5
+
6
+ from sql import Excluded
7
+
8
+
9
+ class TestExcluded(unittest.TestCase):
10
+
11
+ def test_alias(self):
12
+ self.assertEqual(Excluded.alias, 'EXCLUDED')
13
+
14
+ def test_has_alias(self):
15
+ self.assertFalse(Excluded.has_alias)
@@ -0,0 +1,17 @@
1
+ # This file is part of python-sql. The COPYRIGHT file at the top level of
2
+ # this repository contains the full copyright notices and license terms.
3
+
4
+ import unittest
5
+
6
+ from sql import Expression
7
+
8
+
9
+ class TestExpression(unittest.TestCase):
10
+
11
+ def test_str(self):
12
+ with self.assertRaises(NotImplementedError):
13
+ str(Expression())
14
+
15
+ def test_params(self):
16
+ with self.assertRaises(NotImplementedError):
17
+ Expression().params
@@ -0,0 +1,46 @@
1
+ # This file is part of python-sql. The COPYRIGHT file at the top level of
2
+ # this repository contains the full copyright notices and license terms.
3
+
4
+ import unittest
5
+
6
+ from sql import Flavor
7
+
8
+
9
+ class TestFlavor(unittest.TestCase):
10
+
11
+ def test(self):
12
+ Flavor()
13
+
14
+ def test_limitstyle(self):
15
+ flavor = Flavor(limitstyle='rownum')
16
+
17
+ self.assertEqual(flavor.limitstyle, 'rownum')
18
+
19
+ def test_invalid_limitstyle(self):
20
+ with self.assertRaises(ValueError):
21
+ Flavor(limitstyle='foo')
22
+
23
+ def test_max_limit(self):
24
+ flavor = Flavor(max_limit=42)
25
+
26
+ self.assertEqual(flavor.max_limit, 42)
27
+
28
+ def test_invalid_max_limit(self):
29
+ with self.assertRaises(ValueError):
30
+ Flavor(max_limit='foo')
31
+
32
+ def test_paramstyle_format(self):
33
+ flavor = Flavor(paramstyle='format')
34
+
35
+ self.assertEqual(flavor.paramstyle, 'format')
36
+ self.assertEqual(flavor.param, '%s')
37
+
38
+ def test_paramstyle_qmark(self):
39
+ flavor = Flavor(paramstyle='qmark')
40
+
41
+ self.assertEqual(flavor.paramstyle, 'qmark')
42
+ self.assertEqual(flavor.param, '?')
43
+
44
+ def test_invalid_paramstyle(self):
45
+ with self.assertRaises(ValueError):
46
+ Flavor(paramstyle='foo')
sql/tests/test_for.py CHANGED
@@ -14,3 +14,7 @@ class TestFor(unittest.TestCase):
14
14
  for_ = For('UPDATE')
15
15
  for_.tables = Table('t1')
16
16
  self.assertEqual(str(for_), 'FOR UPDATE OF "t1"')
17
+
18
+ def test_invalid_type(self):
19
+ with self.assertRaises(ValueError):
20
+ For('foo')
sql/tests/test_from.py ADDED
@@ -0,0 +1,25 @@
1
+ # This file is part of python-sql. The COPYRIGHT file at the top level of
2
+ # this repository contains the full copyright notices and license terms.
3
+
4
+ import unittest
5
+
6
+ from sql import CombiningQuery, From, Table
7
+
8
+
9
+ class TestFrom(unittest.TestCase):
10
+
11
+ def test_add(self):
12
+ t1 = Table('t1')
13
+ t2 = Table('t2')
14
+ from_ = From([t1]) + t2
15
+
16
+ self.assertEqual(from_, [t1, t2])
17
+
18
+ def test_invalid_add(self):
19
+ with self.assertRaises(TypeError):
20
+ From([Table('t')]) + 'foo'
21
+
22
+ def test_invalid_add_combining_query(self):
23
+ with self.assertRaises(TypeError):
24
+ From([Table('t')]) + CombiningQuery(
25
+ Table('t1').select(), Table('t2').select())
@@ -0,0 +1,46 @@
1
+ # This file is part of python-sql. The COPYRIGHT file at the top level of
2
+ # this repository contains the full copyright notices and license terms.
3
+
4
+ import unittest
5
+
6
+ from sql import AliasManager, Column, From, FromItem
7
+
8
+
9
+ class TestFromItem(unittest.TestCase):
10
+
11
+ def test_from_item(self):
12
+ from_item = FromItem()
13
+
14
+ with AliasManager():
15
+ self.assertFalse(from_item.has_alias)
16
+ from_item.alias
17
+ self.assertTrue(from_item.has_alias)
18
+
19
+ def test_get_column(self):
20
+ from_item = FromItem()
21
+
22
+ foo = from_item.foo
23
+
24
+ self.assertIsInstance(foo, Column)
25
+ self.assertEqual(foo.name, 'foo')
26
+
27
+ def test_get_invalid_column(self):
28
+ from_item = FromItem()
29
+
30
+ with self.assertRaises(AttributeError):
31
+ from_item.__foo__
32
+
33
+ def test_add(self):
34
+ from_item1 = FromItem()
35
+ from_item2 = FromItem()
36
+
37
+ from_ = from_item1 + from_item2
38
+
39
+ self.assertIsInstance(from_, From)
40
+ self.assertEqual(from_, [from_item1, from_item2])
41
+
42
+ def test_invalid_add(self):
43
+ from_item = FromItem()
44
+
45
+ with self.assertRaises(TypeError):
46
+ from_item + 'foo'
@@ -5,12 +5,16 @@ import unittest
5
5
  from sql import AliasManager, Flavor, Table, Window
6
6
  from sql.functions import (
7
7
  Abs, AtTimeZone, CurrentTime, Div, Function, FunctionKeyword,
8
- FunctionNotCallable, Overlay, Rank, Trim)
8
+ FunctionNotCallable, Overlay, Rank, Trim, WindowFunction)
9
9
 
10
10
 
11
11
  class TestFunctions(unittest.TestCase):
12
12
  table = Table('t')
13
13
 
14
+ def test_invalid_columns_definitions(self):
15
+ with self.assertRaises(ValueError):
16
+ Function(columns_definitions='foo')
17
+
14
18
  def test_abs(self):
15
19
  abs_ = Abs(self.table.c1)
16
20
  self.assertEqual(str(abs_), 'ABS("c1")')
@@ -87,6 +91,10 @@ class TestFunctions(unittest.TestCase):
87
91
  self.assertEqual(str(trim), 'TRIM(BOTH %s FROM "c1")')
88
92
  self.assertEqual(trim.params, (' ',))
89
93
 
94
+ def test_trim_invalid_position(self):
95
+ with self.assertRaises(ValueError):
96
+ Trim('test', 'foo')
97
+
90
98
  def test_at_time_zone(self):
91
99
  time_zone = AtTimeZone(self.table.c1, 'UTC')
92
100
  self.assertEqual(str(time_zone), '"c1" AT TIME ZONE %s')
@@ -142,6 +150,10 @@ class TestWindowFunction(unittest.TestCase):
142
150
  self.assertEqual(str(function), 'RANK("a"."c") OVER ()')
143
151
  self.assertEqual(function.params, ())
144
152
 
153
+ def test_invalid_window(self):
154
+ with self.assertRaises(ValueError):
155
+ WindowFunction(window='foo')
156
+
145
157
  def test_filter(self):
146
158
  t = Table('t')
147
159
  function = Rank(t.c, filter_=t.c > 0, window=Window([]))
@@ -150,3 +162,7 @@ class TestWindowFunction(unittest.TestCase):
150
162
  self.assertEqual(str(function),
151
163
  'RANK("a"."c") FILTER (WHERE ("a"."c" > %s)) OVER ()')
152
164
  self.assertEqual(function.params, (0,))
165
+
166
+ def test_invalid_filter(self):
167
+ with self.assertRaises(ValueError):
168
+ WindowFunction(filter_='foo', window=Window([]))
@@ -0,0 +1,13 @@
1
+ # This file is part of python-sql. The COPYRIGHT file at the top level of
2
+ # this repository contains the full copyright notices and license terms.
3
+
4
+ import unittest
5
+
6
+ from sql import Grouping
7
+
8
+
9
+ class TestGrouping(unittest.TestCase):
10
+
11
+ def test_invalid_sets(self):
12
+ with self.assertRaises(ValueError):
13
+ Grouping('foo')
sql/tests/test_insert.py CHANGED
@@ -2,13 +2,25 @@
2
2
  # this repository contains the full copyright notices and license terms.
3
3
  import unittest
4
4
 
5
- from sql import Conflict, Excluded, Table, With
5
+ from sql import Conflict, Excluded, Insert, Table, With
6
6
  from sql.functions import Abs
7
7
 
8
8
 
9
9
  class TestInsert(unittest.TestCase):
10
10
  table = Table('t')
11
11
 
12
+ def test_insert_invalid_table(self):
13
+ with self.assertRaises(ValueError):
14
+ Insert('foo')
15
+
16
+ def test_insert_invalid_columns(self):
17
+ with self.assertRaises(ValueError):
18
+ self.table.insert(['foo'], [['foo']])
19
+
20
+ def test_insert_invalid_values(self):
21
+ with self.assertRaises(ValueError):
22
+ self.table.insert([self.table.c], 'foo')
23
+
12
24
  def test_insert_default(self):
13
25
  query = self.table.insert()
14
26
  self.assertEqual(str(query), 'INSERT INTO "t" DEFAULT VALUES')
@@ -64,6 +76,10 @@ class TestInsert(unittest.TestCase):
64
76
  'WHERE (("a"."c1" = "b"."c") AND ("a"."c2" = %s)))')
65
77
  self.assertEqual(tuple(query.params), ('foo', 'bar'))
66
78
 
79
+ def test_insert_invalid_returning(self):
80
+ with self.assertRaises(ValueError):
81
+ self.table.insert(returning='foo')
82
+
67
83
  def test_with(self):
68
84
  t1 = Table('t1')
69
85
  w = With(query=t1.select())
@@ -104,6 +120,14 @@ class TestInsert(unittest.TestCase):
104
120
  'INSERT INTO "default"."t1" ("c1") VALUES (%s)')
105
121
  self.assertEqual(tuple(query.params), ('foo',))
106
122
 
123
+ def test_upsert_invalid_on_conflict(self):
124
+ with self.assertRaises(ValueError):
125
+ self.table.insert(on_conflict='foo')
126
+
127
+ def test_upsert_invalid_table_on_conflict(self):
128
+ with self.assertRaises(ValueError):
129
+ self.table.insert(on_conflict=Conflict(Table('t1')))
130
+
107
131
  def test_upsert_nothing(self):
108
132
  query = self.table.insert(
109
133
  [self.table.c1], [['foo']],
@@ -196,3 +220,35 @@ class TestInsert(unittest.TestCase):
196
220
  'INSERT INTO "t" AS "a" ("c1") VALUES (%s) '
197
221
  'ON CONFLICT DO UPDATE SET "c1" = (("EXCLUDED"."c1" + %s))')
198
222
  self.assertEqual(tuple(query.params), (1, 2))
223
+
224
+ def test_conflict_invalid_table(self):
225
+ with self.assertRaises(ValueError):
226
+ Conflict('foo')
227
+
228
+ def test_conflict_invalid_indexed_columns(self):
229
+ with self.assertRaises(ValueError):
230
+ Conflict(self.table, indexed_columns=['foo'])
231
+
232
+ def test_conflict_indexed_columns_invalid_table(self):
233
+ with self.assertRaises(ValueError):
234
+ Conflict(self.table, indexed_columns=[Table('t').c])
235
+
236
+ def test_conflict_invalid_index_where(self):
237
+ with self.assertRaises(ValueError):
238
+ Conflict(self.table, index_where='foo')
239
+
240
+ def test_conflict_invalid_columns(self):
241
+ with self.assertRaises(ValueError):
242
+ Conflict(self.table, columns=['foo'])
243
+
244
+ def test_conflict_columns_invalid_table(self):
245
+ with self.assertRaises(ValueError):
246
+ Conflict(self.table, columns=[Table('t').c])
247
+
248
+ def test_conflict_invalid_values(self):
249
+ with self.assertRaises(ValueError):
250
+ Conflict(self.table, values='foo')
251
+
252
+ def test_conflict_invalid_where(self):
253
+ with self.assertRaises(ValueError):
254
+ Conflict(self.table, where='foo')
sql/tests/test_join.py CHANGED
@@ -20,6 +20,22 @@ class TestJoin(unittest.TestCase):
20
20
  self.assertEqual(str(join),
21
21
  '"t1" AS "a" INNER JOIN "t2" AS "b" ON ("a"."c" = "b"."c")')
22
22
 
23
+ def test_join_invalid_left(self):
24
+ with self.assertRaises(ValueError):
25
+ Join('foo', Table('t1'))
26
+
27
+ def test_join_invalid_right(self):
28
+ with self.assertRaises(ValueError):
29
+ Join(Table('t1'), 'foo')
30
+
31
+ def test_join_invalid_condition(self):
32
+ with self.assertRaises(ValueError):
33
+ Join(Table('t1'), Table('t2'), condition='foo')
34
+
35
+ def test_join_invalid_type(self):
36
+ with self.assertRaises(ValueError):
37
+ Join(Table('t1'), Table('t2'), type_='foo')
38
+
23
39
  def test_join_subselect(self):
24
40
  t1 = Table('t1')
25
41
  t2 = Table('t2')
@@ -50,3 +66,10 @@ class TestJoin(unittest.TestCase):
50
66
  join = getattr(t1, method)(t2)
51
67
  type_ = method[:-len('_join')].replace('_', ' ').upper()
52
68
  self.assertEqual(join.type_, type_)
69
+
70
+ def test_join_alias(self):
71
+ join = Join(Table('t1'), Table('t2'))
72
+ with self.assertRaises(AttributeError):
73
+ join.alias
74
+ with self.assertRaises(AttributeError):
75
+ join.has_alias
sql/tests/test_lateral.py CHANGED
@@ -11,7 +11,7 @@ class TestLateral(unittest.TestCase):
11
11
  def test_lateral_select(self):
12
12
  t1 = Table('t1')
13
13
  t2 = Table('t2')
14
- lateral = Lateral(t2.select(where=t2.id == t1.t2))
14
+ lateral = t2.select(where=t2.id == t1.t2).lateral()
15
15
  query = From([t1, lateral]).select()
16
16
 
17
17
  self.assertEqual(str(query),