piccolo 1.7.0__py3-none-any.whl → 1.9.0__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.
@@ -768,15 +768,15 @@ class TestMigrationManager(DBTestCase):
768
768
  )
769
769
 
770
770
  asyncio.run(manager.run())
771
- self.assertEqual(
772
- self._get_column_default(),
773
- [{"column_default": "'Unknown':::STRING"}],
771
+ self.assertIn(
772
+ self._get_column_default()[0]["column_default"],
773
+ ["'Unknown'", "'Unknown':::STRING"],
774
774
  )
775
775
 
776
776
  asyncio.run(manager.run(backwards=True))
777
- self.assertEqual(
778
- self._get_column_default(),
779
- [{"column_default": "'':::STRING"}],
777
+ self.assertIn(
778
+ self._get_column_default()[0]["column_default"],
779
+ ["''", "'':::STRING"],
780
780
  )
781
781
 
782
782
  @engines_only("postgres")
@@ -856,9 +856,9 @@ class TestMigrationManager(DBTestCase):
856
856
  old_params={"default": None},
857
857
  )
858
858
  asyncio.run(manager_1.run())
859
- self.assertEqual(
860
- self._get_column_default(),
861
- [{"column_default": "'Mr Manager':::STRING"}],
859
+ self.assertIn(
860
+ self._get_column_default()[0]["column_default"],
861
+ ["'Mr Manager'", "'Mr Manager':::STRING"],
862
862
  )
863
863
 
864
864
  # Drop the default.
@@ -879,9 +879,9 @@ class TestMigrationManager(DBTestCase):
879
879
  # And add it back once more to be sure.
880
880
  manager_3 = manager_1
881
881
  asyncio.run(manager_3.run())
882
- self.assertEqual(
883
- self._get_column_default(),
884
- [{"column_default": "'Mr Manager':::STRING"}],
882
+ self.assertIn(
883
+ self._get_column_default()[0]["column_default"],
884
+ ["'Mr Manager'", "'Mr Manager':::STRING"],
885
885
  )
886
886
 
887
887
  # Run them all backwards
@@ -892,9 +892,9 @@ class TestMigrationManager(DBTestCase):
892
892
  )
893
893
 
894
894
  asyncio.run(manager_2.run(backwards=True))
895
- self.assertEqual(
896
- self._get_column_default(),
897
- [{"column_default": "'Mr Manager':::STRING"}],
895
+ self.assertIn(
896
+ self._get_column_default()[0]["column_default"],
897
+ ["'Mr Manager'", "'Mr Manager':::STRING"],
898
898
  )
899
899
 
900
900
  asyncio.run(manager_1.run(backwards=True))
@@ -1,6 +1,8 @@
1
1
  import datetime
2
2
  from unittest import TestCase
3
3
 
4
+ import pytest
5
+
4
6
  from piccolo.columns.column_types import (
5
7
  Array,
6
8
  BigInt,
@@ -10,8 +12,9 @@ from piccolo.columns.column_types import (
10
12
  Timestamp,
11
13
  Timestamptz,
12
14
  )
15
+ from piccolo.querystring import QueryString
13
16
  from piccolo.table import Table
14
- from tests.base import engines_only, sqlite_only
17
+ from tests.base import engines_only, engines_skip, sqlite_only
15
18
 
16
19
 
17
20
  class MyTable(Table):
@@ -40,12 +43,18 @@ class TestArray(TestCase):
40
43
  def tearDown(self):
41
44
  MyTable.alter().drop_table().run_sync()
42
45
 
43
- @engines_only("postgres", "sqlite")
46
+ @pytest.mark.cockroach_array_slow
44
47
  def test_storage(self):
45
48
  """
46
49
  Make sure data can be stored and retrieved.
47
50
 
48
- 🐛 Cockroach bug: https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
51
+ In CockroachDB <= v22.2.0 we had this error:
52
+
53
+ * https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
54
+
55
+ In newer CockroachDB versions, it runs but is very slow:
56
+
57
+ * https://github.com/piccolo-orm/piccolo/issues/1005
49
58
 
50
59
  """ # noqa: E501
51
60
  MyTable(value=[1, 2, 3]).save().run_sync()
@@ -54,12 +63,19 @@ class TestArray(TestCase):
54
63
  assert row is not None
55
64
  self.assertEqual(row.value, [1, 2, 3])
56
65
 
57
- @engines_only("postgres")
66
+ @engines_skip("sqlite")
67
+ @pytest.mark.cockroach_array_slow
58
68
  def test_index(self):
59
69
  """
60
70
  Indexes should allow individual array elements to be queried.
61
71
 
62
- 🐛 Cockroach bug: https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
72
+ In CockroachDB <= v22.2.0 we had this error:
73
+
74
+ * https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
75
+
76
+ In newer CockroachDB versions, it runs but is very slow:
77
+
78
+ * https://github.com/piccolo-orm/piccolo/issues/1005
63
79
 
64
80
  """ # noqa: E501
65
81
  MyTable(value=[1, 2, 3]).save().run_sync()
@@ -68,66 +84,92 @@ class TestArray(TestCase):
68
84
  MyTable.select(MyTable.value[0]).first().run_sync(), {"value": 1}
69
85
  )
70
86
 
71
- @engines_only("postgres")
87
+ @engines_skip("sqlite")
88
+ @pytest.mark.cockroach_array_slow
72
89
  def test_all(self):
73
90
  """
74
91
  Make sure rows can be retrieved where all items in an array match a
75
92
  given value.
76
93
 
77
- 🐛 Cockroach bug: https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
94
+ In CockroachDB <= v22.2.0 we had this error:
95
+
96
+ * https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
97
+
98
+ In newer CockroachDB versions, it runs but is very slow:
99
+
100
+ * https://github.com/piccolo-orm/piccolo/issues/1005
78
101
 
79
102
  """ # noqa: E501
80
103
  MyTable(value=[1, 1, 1]).save().run_sync()
81
104
 
105
+ # We have to explicitly specify the type, so CockroachDB works.
82
106
  self.assertEqual(
83
107
  MyTable.select(MyTable.value)
84
- .where(MyTable.value.all(1))
108
+ .where(MyTable.value.all(QueryString("{}::INTEGER", 1)))
85
109
  .first()
86
110
  .run_sync(),
87
111
  {"value": [1, 1, 1]},
88
112
  )
89
113
 
114
+ # We have to explicitly specify the type, so CockroachDB works.
90
115
  self.assertEqual(
91
116
  MyTable.select(MyTable.value)
92
- .where(MyTable.value.all(0))
117
+ .where(MyTable.value.all(QueryString("{}::INTEGER", 0)))
93
118
  .first()
94
119
  .run_sync(),
95
120
  None,
96
121
  )
97
122
 
98
- @engines_only("postgres")
123
+ @engines_skip("sqlite")
124
+ @pytest.mark.cockroach_array_slow
99
125
  def test_any(self):
100
126
  """
101
127
  Make sure rows can be retrieved where any items in an array match a
102
128
  given value.
103
129
 
104
- 🐛 Cockroach bug: https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
130
+ In CockroachDB <= v22.2.0 we had this error:
131
+
132
+ * https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
133
+
134
+ In newer CockroachDB versions, it runs but is very slow:
135
+
136
+ * https://github.com/piccolo-orm/piccolo/issues/1005
105
137
 
106
138
  """ # noqa: E501
139
+
107
140
  MyTable(value=[1, 2, 3]).save().run_sync()
108
141
 
142
+ # We have to explicitly specify the type, so CockroachDB works.
109
143
  self.assertEqual(
110
144
  MyTable.select(MyTable.value)
111
- .where(MyTable.value.any(1))
145
+ .where(MyTable.value.any(QueryString("{}::INTEGER", 1)))
112
146
  .first()
113
147
  .run_sync(),
114
148
  {"value": [1, 2, 3]},
115
149
  )
116
150
 
151
+ # We have to explicitly specify the type, so CockroachDB works.
117
152
  self.assertEqual(
118
153
  MyTable.select(MyTable.value)
119
- .where(MyTable.value.any(0))
154
+ .where(MyTable.value.any(QueryString("{}::INTEGER", 0)))
120
155
  .first()
121
156
  .run_sync(),
122
157
  None,
123
158
  )
124
159
 
125
- @engines_only("postgres")
160
+ @engines_skip("sqlite")
161
+ @pytest.mark.cockroach_array_slow
126
162
  def test_cat(self):
127
163
  """
128
164
  Make sure values can be appended to an array.
129
165
 
130
- 🐛 Cockroach bug: https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
166
+ In CockroachDB <= v22.2.0 we had this error:
167
+
168
+ * https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
169
+
170
+ In newer CockroachDB versions, it runs but is very slow:
171
+
172
+ * https://github.com/piccolo-orm/piccolo/issues/1005
131
173
 
132
174
  """ # noqa: E501
133
175
  MyTable(value=[1, 1, 1]).save().run_sync()
@@ -137,7 +179,8 @@ class TestArray(TestCase):
137
179
  ).run_sync()
138
180
 
139
181
  self.assertEqual(
140
- MyTable.select().run_sync(), [{"id": 1, "value": [1, 1, 1, 2]}]
182
+ MyTable.select(MyTable.value).run_sync(),
183
+ [{"value": [1, 1, 1, 2]}],
141
184
  )
142
185
 
143
186
  # Try plus symbol
@@ -147,7 +190,8 @@ class TestArray(TestCase):
147
190
  ).run_sync()
148
191
 
149
192
  self.assertEqual(
150
- MyTable.select().run_sync(), [{"id": 1, "value": [1, 1, 1, 2, 3]}]
193
+ MyTable.select(MyTable.value).run_sync(),
194
+ [{"value": [1, 1, 1, 2, 3]}],
151
195
  )
152
196
 
153
197
  # Make sure non-list values work
@@ -157,8 +201,8 @@ class TestArray(TestCase):
157
201
  ).run_sync()
158
202
 
159
203
  self.assertEqual(
160
- MyTable.select().run_sync(),
161
- [{"id": 1, "value": [1, 1, 1, 2, 3, 4]}],
204
+ MyTable.select(MyTable.value).run_sync(),
205
+ [{"value": [1, 1, 1, 2, 3, 4]}],
162
206
  )
163
207
 
164
208
  @sqlite_only
@@ -0,0 +1,66 @@
1
+ import datetime
2
+ from unittest import TestCase
3
+
4
+ from tests.base import engines_only
5
+ from tests.example_apps.music.tables import Band
6
+
7
+
8
+ @engines_only("postgres", "cockroach")
9
+ class TestArrayPostgres(TestCase):
10
+
11
+ def test_string(self):
12
+ self.assertEqual(
13
+ Band.name.get_sql_value(["a", "b", "c"]),
14
+ '\'{"a","b","c"}\'',
15
+ )
16
+
17
+ def test_int(self):
18
+ self.assertEqual(
19
+ Band.name.get_sql_value([1, 2, 3]),
20
+ "'{1,2,3}'",
21
+ )
22
+
23
+ def test_nested(self):
24
+ self.assertEqual(
25
+ Band.name.get_sql_value([1, 2, 3, [4, 5, 6]]),
26
+ "'{1,2,3,{4,5,6}}'",
27
+ )
28
+
29
+ def test_time(self):
30
+ self.assertEqual(
31
+ Band.name.get_sql_value([datetime.time(hour=8, minute=0)]),
32
+ "'{\"08:00:00\"}'",
33
+ )
34
+
35
+
36
+ @engines_only("sqlite")
37
+ class TestArraySQLite(TestCase):
38
+ """
39
+ Note, we use ``.replace(" ", "")`` because we serialise arrays using
40
+ Python's json library, and there is inconsistency between Python versions
41
+ (some output ``["a", "b", "c"]``, and others ``["a","b","c"]``).
42
+ """
43
+
44
+ def test_string(self):
45
+ self.assertEqual(
46
+ Band.name.get_sql_value(["a", "b", "c"]).replace(" ", ""),
47
+ '\'["a","b","c"]\'',
48
+ )
49
+
50
+ def test_int(self):
51
+ self.assertEqual(
52
+ Band.name.get_sql_value([1, 2, 3]).replace(" ", ""),
53
+ "'[1,2,3]'",
54
+ )
55
+
56
+ def test_nested(self):
57
+ self.assertEqual(
58
+ Band.name.get_sql_value([1, 2, 3, [4, 5, 6]]).replace(" ", ""),
59
+ "'[1,2,3,[4,5,6]]'",
60
+ )
61
+
62
+ def test_time(self):
63
+ self.assertEqual(
64
+ Band.name.get_sql_value([datetime.time(hour=8, minute=0)]),
65
+ "'[\"08:00:00\"]'",
66
+ )
File without changes
@@ -0,0 +1,34 @@
1
+ import typing as t
2
+ from unittest import TestCase
3
+
4
+ from piccolo.table import Table, create_db_tables_sync, drop_db_tables_sync
5
+ from tests.example_apps.music.tables import Band, Manager
6
+
7
+
8
+ class FunctionTest(TestCase):
9
+ tables: t.List[t.Type[Table]]
10
+
11
+ def setUp(self) -> None:
12
+ create_db_tables_sync(*self.tables)
13
+
14
+ def tearDown(self) -> None:
15
+ drop_db_tables_sync(*self.tables)
16
+
17
+
18
+ class BandTest(FunctionTest):
19
+ tables = [Band, Manager]
20
+
21
+ def setUp(self) -> None:
22
+ super().setUp()
23
+
24
+ manager = Manager({Manager.name: "Guido"})
25
+ manager.save().run_sync()
26
+
27
+ band = Band(
28
+ {
29
+ Band.name: "Pythonistas",
30
+ Band.manager: manager,
31
+ Band.popularity: 1000,
32
+ }
33
+ )
34
+ band.save().run_sync()
@@ -1,51 +1,13 @@
1
- from unittest import TestCase
2
-
3
- from piccolo.query.functions.string import Reverse, Upper
1
+ from piccolo.query.functions import Reverse, Upper
4
2
  from piccolo.querystring import QueryString
5
- from piccolo.table import create_db_tables_sync, drop_db_tables_sync
6
3
  from tests.base import engines_skip
7
- from tests.example_apps.music.tables import Band, Manager
8
-
9
-
10
- class FunctionTest(TestCase):
11
- tables = (Band, Manager)
12
-
13
- def setUp(self) -> None:
14
- create_db_tables_sync(*self.tables)
15
-
16
- manager = Manager({Manager.name: "Guido"})
17
- manager.save().run_sync()
18
-
19
- band = Band({Band.name: "Pythonistas", Band.manager: manager})
20
- band.save().run_sync()
21
-
22
- def tearDown(self) -> None:
23
- drop_db_tables_sync(*self.tables)
24
-
4
+ from tests.example_apps.music.tables import Band
25
5
 
26
- class TestUpperFunction(FunctionTest):
27
-
28
- def test_column(self):
29
- """
30
- Make sure we can uppercase a column's value.
31
- """
32
- response = Band.select(Upper(Band.name)).run_sync()
33
- self.assertListEqual(response, [{"upper": "PYTHONISTAS"}])
34
-
35
- def test_alias(self):
36
- response = Band.select(Upper(Band.name, alias="name")).run_sync()
37
- self.assertListEqual(response, [{"name": "PYTHONISTAS"}])
38
-
39
- def test_joined_column(self):
40
- """
41
- Make sure we can uppercase a column's value from a joined table.
42
- """
43
- response = Band.select(Upper(Band.manager._.name)).run_sync()
44
- self.assertListEqual(response, [{"upper": "GUIDO"}])
6
+ from .base import BandTest
45
7
 
46
8
 
47
9
  @engines_skip("sqlite")
48
- class TestNested(FunctionTest):
10
+ class TestNested(BandTest):
49
11
  """
50
12
  Skip the the test for SQLite, as it doesn't support ``Reverse``.
51
13
  """
@@ -76,7 +38,7 @@ class TestNested(FunctionTest):
76
38
  self.assertListEqual(response, [{"concat": "GUIDO!"}])
77
39
 
78
40
 
79
- class TestWhereClause(FunctionTest):
41
+ class TestWhereClause(BandTest):
80
42
 
81
43
  def test_where(self):
82
44
  """
@@ -0,0 +1,39 @@
1
+ import decimal
2
+
3
+ from piccolo.columns import Numeric
4
+ from piccolo.query.functions.math import Abs, Ceil, Floor, Round
5
+ from piccolo.table import Table
6
+
7
+ from .base import FunctionTest
8
+
9
+
10
+ class Ticket(Table):
11
+ price = Numeric(digits=(5, 2))
12
+
13
+
14
+ class TestMath(FunctionTest):
15
+
16
+ tables = [Ticket]
17
+
18
+ def setUp(self):
19
+ super().setUp()
20
+ self.ticket = Ticket({Ticket.price: decimal.Decimal("36.50")})
21
+ self.ticket.save().run_sync()
22
+
23
+ def test_floor(self):
24
+ response = Ticket.select(Floor(Ticket.price, alias="price")).run_sync()
25
+ self.assertListEqual(response, [{"price": decimal.Decimal("36.00")}])
26
+
27
+ def test_ceil(self):
28
+ response = Ticket.select(Ceil(Ticket.price, alias="price")).run_sync()
29
+ self.assertListEqual(response, [{"price": decimal.Decimal("37.00")}])
30
+
31
+ def test_abs(self):
32
+ self.ticket.price = decimal.Decimal("-1.50")
33
+ self.ticket.save().run_sync()
34
+ response = Ticket.select(Abs(Ticket.price, alias="price")).run_sync()
35
+ self.assertListEqual(response, [{"price": decimal.Decimal("1.50")}])
36
+
37
+ def test_round(self):
38
+ response = Ticket.select(Round(Ticket.price, alias="price")).run_sync()
39
+ self.assertListEqual(response, [{"price": decimal.Decimal("37.00")}])
@@ -0,0 +1,25 @@
1
+ from piccolo.query.functions.string import Upper
2
+ from tests.example_apps.music.tables import Band
3
+
4
+ from .base import BandTest
5
+
6
+
7
+ class TestUpperFunction(BandTest):
8
+
9
+ def test_column(self):
10
+ """
11
+ Make sure we can uppercase a column's value.
12
+ """
13
+ response = Band.select(Upper(Band.name)).run_sync()
14
+ self.assertListEqual(response, [{"upper": "PYTHONISTAS"}])
15
+
16
+ def test_alias(self):
17
+ response = Band.select(Upper(Band.name, alias="name")).run_sync()
18
+ self.assertListEqual(response, [{"name": "PYTHONISTAS"}])
19
+
20
+ def test_joined_column(self):
21
+ """
22
+ Make sure we can uppercase a column's value from a joined table.
23
+ """
24
+ response = Band.select(Upper(Band.manager._.name)).run_sync()
25
+ self.assertListEqual(response, [{"upper": "GUIDO"}])
@@ -0,0 +1,134 @@
1
+ from piccolo.columns import Integer, Text, Varchar
2
+ from piccolo.query.functions import Cast, Length
3
+ from tests.example_apps.music.tables import Band, Manager
4
+
5
+ from .base import BandTest
6
+
7
+
8
+ class TestCast(BandTest):
9
+ def test_varchar(self):
10
+ """
11
+ Make sure that casting to ``Varchar`` works.
12
+ """
13
+ response = Band.select(
14
+ Cast(
15
+ Band.popularity,
16
+ as_type=Varchar(),
17
+ )
18
+ ).run_sync()
19
+
20
+ self.assertListEqual(
21
+ response,
22
+ [{"popularity": "1000"}],
23
+ )
24
+
25
+ def test_text(self):
26
+ """
27
+ Make sure that casting to ``Text`` works.
28
+ """
29
+ response = Band.select(
30
+ Cast(
31
+ Band.popularity,
32
+ as_type=Text(),
33
+ )
34
+ ).run_sync()
35
+
36
+ self.assertListEqual(
37
+ response,
38
+ [{"popularity": "1000"}],
39
+ )
40
+
41
+ def test_integer(self):
42
+ """
43
+ Make sure that casting to ``Integer`` works.
44
+ """
45
+ Band.update({Band.name: "1111"}, force=True).run_sync()
46
+
47
+ response = Band.select(
48
+ Cast(
49
+ Band.name,
50
+ as_type=Integer(),
51
+ )
52
+ ).run_sync()
53
+
54
+ self.assertListEqual(
55
+ response,
56
+ [{"name": 1111}],
57
+ )
58
+
59
+ def test_join(self):
60
+ """
61
+ Make sure that casting works with joins.
62
+ """
63
+ Manager.update({Manager.name: "1111"}, force=True).run_sync()
64
+
65
+ response = Band.select(
66
+ Band.name,
67
+ Cast(
68
+ Band.manager.name,
69
+ as_type=Integer(),
70
+ ),
71
+ ).run_sync()
72
+
73
+ self.assertListEqual(
74
+ response,
75
+ [
76
+ {
77
+ "name": "Pythonistas",
78
+ "manager.name": 1111,
79
+ }
80
+ ],
81
+ )
82
+
83
+ def test_nested_inner(self):
84
+ """
85
+ Make sure ``Cast`` can be passed into other functions.
86
+ """
87
+ Band.update({Band.name: "1111"}, force=True).run_sync()
88
+
89
+ response = Band.select(
90
+ Length(
91
+ Cast(
92
+ Band.popularity,
93
+ as_type=Varchar(),
94
+ )
95
+ )
96
+ ).run_sync()
97
+
98
+ self.assertListEqual(
99
+ response,
100
+ [{"length": 4}],
101
+ )
102
+
103
+ def test_nested_outer(self):
104
+ """
105
+ Make sure a querystring can be passed into ``Cast`` (meaning it can be
106
+ nested).
107
+ """
108
+ response = Band.select(
109
+ Cast(
110
+ Length(Band.name),
111
+ as_type=Varchar(),
112
+ alias="length",
113
+ )
114
+ ).run_sync()
115
+
116
+ self.assertListEqual(
117
+ response,
118
+ [{"length": str(len("Pythonistas"))}],
119
+ )
120
+
121
+ def test_where_clause(self):
122
+ """
123
+ Make sure ``Cast`` works in a where clause.
124
+ """
125
+ response = (
126
+ Band.select(Band.name, Band.popularity)
127
+ .where(Cast(Band.popularity, Varchar()) == "1000")
128
+ .run_sync()
129
+ )
130
+
131
+ self.assertListEqual(
132
+ response,
133
+ [{"name": "Pythonistas", "popularity": 1000}],
134
+ )