pixeltable 0.2.5__py3-none-any.whl → 0.2.7__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.

Potentially problematic release.


This version of pixeltable might be problematic. Click here for more details.

Files changed (110) hide show
  1. pixeltable/__init__.py +20 -9
  2. pixeltable/__version__.py +3 -0
  3. pixeltable/catalog/column.py +23 -7
  4. pixeltable/catalog/insertable_table.py +32 -19
  5. pixeltable/catalog/table.py +210 -20
  6. pixeltable/catalog/table_version.py +272 -111
  7. pixeltable/catalog/table_version_path.py +6 -1
  8. pixeltable/dataframe.py +184 -110
  9. pixeltable/datatransfer/__init__.py +1 -0
  10. pixeltable/datatransfer/label_studio.py +526 -0
  11. pixeltable/datatransfer/remote.py +113 -0
  12. pixeltable/env.py +213 -79
  13. pixeltable/exec/__init__.py +2 -1
  14. pixeltable/exec/data_row_batch.py +6 -7
  15. pixeltable/exec/expr_eval_node.py +28 -28
  16. pixeltable/exec/sql_scan_node.py +7 -6
  17. pixeltable/exprs/__init__.py +4 -3
  18. pixeltable/exprs/column_ref.py +11 -2
  19. pixeltable/exprs/comparison.py +39 -1
  20. pixeltable/exprs/data_row.py +7 -0
  21. pixeltable/exprs/expr.py +26 -19
  22. pixeltable/exprs/function_call.py +17 -18
  23. pixeltable/exprs/globals.py +14 -2
  24. pixeltable/exprs/image_member_access.py +9 -28
  25. pixeltable/exprs/in_predicate.py +96 -0
  26. pixeltable/exprs/inline_array.py +13 -11
  27. pixeltable/exprs/inline_dict.py +15 -13
  28. pixeltable/exprs/row_builder.py +7 -1
  29. pixeltable/exprs/similarity_expr.py +67 -0
  30. pixeltable/ext/functions/whisperx.py +30 -0
  31. pixeltable/ext/functions/yolox.py +16 -0
  32. pixeltable/func/__init__.py +0 -2
  33. pixeltable/func/aggregate_function.py +5 -2
  34. pixeltable/func/callable_function.py +57 -13
  35. pixeltable/func/expr_template_function.py +14 -3
  36. pixeltable/func/function.py +35 -4
  37. pixeltable/func/signature.py +5 -15
  38. pixeltable/func/udf.py +8 -12
  39. pixeltable/functions/fireworks.py +9 -4
  40. pixeltable/functions/huggingface.py +48 -5
  41. pixeltable/functions/openai.py +49 -11
  42. pixeltable/functions/pil/image.py +61 -64
  43. pixeltable/functions/together.py +32 -6
  44. pixeltable/functions/util.py +0 -43
  45. pixeltable/functions/video.py +46 -8
  46. pixeltable/globals.py +443 -0
  47. pixeltable/index/__init__.py +1 -0
  48. pixeltable/index/base.py +9 -2
  49. pixeltable/index/btree.py +54 -0
  50. pixeltable/index/embedding_index.py +91 -15
  51. pixeltable/io/__init__.py +4 -0
  52. pixeltable/io/globals.py +59 -0
  53. pixeltable/{utils → io}/hf_datasets.py +48 -17
  54. pixeltable/io/pandas.py +148 -0
  55. pixeltable/{utils → io}/parquet.py +58 -33
  56. pixeltable/iterators/__init__.py +1 -1
  57. pixeltable/iterators/base.py +8 -4
  58. pixeltable/iterators/document.py +225 -93
  59. pixeltable/iterators/video.py +16 -9
  60. pixeltable/metadata/__init__.py +8 -4
  61. pixeltable/metadata/converters/convert_12.py +3 -0
  62. pixeltable/metadata/converters/convert_13.py +41 -0
  63. pixeltable/metadata/converters/convert_14.py +13 -0
  64. pixeltable/metadata/converters/convert_15.py +29 -0
  65. pixeltable/metadata/converters/util.py +63 -0
  66. pixeltable/metadata/schema.py +12 -6
  67. pixeltable/plan.py +11 -24
  68. pixeltable/store.py +16 -23
  69. pixeltable/tool/create_test_db_dump.py +49 -14
  70. pixeltable/type_system.py +27 -58
  71. pixeltable/utils/coco.py +94 -0
  72. pixeltable/utils/documents.py +42 -12
  73. pixeltable/utils/http_server.py +70 -0
  74. pixeltable-0.2.7.dist-info/METADATA +137 -0
  75. pixeltable-0.2.7.dist-info/RECORD +126 -0
  76. {pixeltable-0.2.5.dist-info → pixeltable-0.2.7.dist-info}/WHEEL +1 -1
  77. pixeltable/client.py +0 -600
  78. pixeltable/exprs/image_similarity_predicate.py +0 -58
  79. pixeltable/func/batched_function.py +0 -53
  80. pixeltable/func/nos_function.py +0 -202
  81. pixeltable/tests/conftest.py +0 -171
  82. pixeltable/tests/ext/test_yolox.py +0 -21
  83. pixeltable/tests/functions/test_fireworks.py +0 -43
  84. pixeltable/tests/functions/test_functions.py +0 -60
  85. pixeltable/tests/functions/test_huggingface.py +0 -158
  86. pixeltable/tests/functions/test_openai.py +0 -162
  87. pixeltable/tests/functions/test_together.py +0 -112
  88. pixeltable/tests/test_audio.py +0 -65
  89. pixeltable/tests/test_catalog.py +0 -27
  90. pixeltable/tests/test_client.py +0 -21
  91. pixeltable/tests/test_component_view.py +0 -379
  92. pixeltable/tests/test_dataframe.py +0 -440
  93. pixeltable/tests/test_dirs.py +0 -107
  94. pixeltable/tests/test_document.py +0 -120
  95. pixeltable/tests/test_exprs.py +0 -802
  96. pixeltable/tests/test_function.py +0 -332
  97. pixeltable/tests/test_index.py +0 -138
  98. pixeltable/tests/test_migration.py +0 -44
  99. pixeltable/tests/test_nos.py +0 -54
  100. pixeltable/tests/test_snapshot.py +0 -231
  101. pixeltable/tests/test_table.py +0 -1343
  102. pixeltable/tests/test_transactional_directory.py +0 -42
  103. pixeltable/tests/test_types.py +0 -52
  104. pixeltable/tests/test_video.py +0 -159
  105. pixeltable/tests/test_view.py +0 -535
  106. pixeltable/tests/utils.py +0 -442
  107. pixeltable/utils/clip.py +0 -18
  108. pixeltable-0.2.5.dist-info/METADATA +0 -128
  109. pixeltable-0.2.5.dist-info/RECORD +0 -139
  110. {pixeltable-0.2.5.dist-info → pixeltable-0.2.7.dist-info}/LICENSE +0 -0
@@ -1,535 +0,0 @@
1
- import datetime
2
- import logging
3
-
4
- import PIL
5
- import pytest
6
-
7
- import pixeltable as pxt
8
- from pixeltable import catalog
9
- from pixeltable import exceptions as excs
10
- from pixeltable.tests.utils import create_test_tbl, assert_resultset_eq
11
- from pixeltable.type_system import IntType, FloatType, ImageType
12
-
13
- logger = logging.getLogger('pixeltable')
14
-
15
- class TestView:
16
- """
17
- TODO:
18
- - test tree of views
19
- - test consecutive component views
20
-
21
- """
22
- def create_tbl(self, cl: pxt.Client) -> catalog.InsertableTable:
23
- """Create table with computed columns"""
24
- t = create_test_tbl(cl)
25
- t.add_column(d1=t.c3 - 1)
26
- # add column that can be updated
27
- t.add_column(c10=FloatType(nullable=True))
28
- t.update({'c10': t.c3})
29
- # computed column that depends on two columns: exercise duplicate elimination during query construction
30
- t.add_column(d2=t.c3 - t.c10)
31
- return t
32
-
33
- def test_basic(self, test_client: pxt.Client) -> None:
34
- cl = test_client
35
- t = self.create_tbl(cl)
36
-
37
- # create view with filter and computed columns
38
- schema = {
39
- 'v1': t.c3 * 2.0,
40
- 'v2': t.c6.f5,
41
- }
42
- v = cl.create_view('test_view', t, schema=schema, filter=t.c2 < 10)
43
- # TODO: test repr more thoroughly
44
- _ = v.__repr__()
45
- assert_resultset_eq(
46
- v.select(v.v1).order_by(v.c2).collect(),
47
- t.select(t.c3 * 2.0).where(t.c2 < 10).order_by(t.c2).collect())
48
- # view-only query; returns the same result
49
- assert_resultset_eq(
50
- v.select(v.v1).order_by(v.v1).collect(),
51
- t.select(t.c3 * 2.0).where(t.c2 < 10).order_by(t.c2).collect())
52
- # computed columns that don't reference the base table
53
- v.add_column(v3=v.v1 * 2.0)
54
- v.add_column(v4=v.v2[0])
55
-
56
- def check_view(t: pxt.Table, v: pxt.Table) -> None:
57
- assert v.count() == t.where(t.c2 < 10).count()
58
- assert_resultset_eq(
59
- v.select(v.v1).order_by(v.c2).collect(),
60
- t.select(t.c3 * 2.0).where(t.c2 < 10).order_by(t.c2).collect())
61
- assert_resultset_eq(
62
- v.select(v.v3).order_by(v.c2).collect(),
63
- t.select(t.c3 * 4.0).where(t.c2 < 10).order_by(t.c2).collect())
64
- assert_resultset_eq(
65
- v.select(v.v4).order_by(v.c2).collect(),
66
- t.select(t.c6.f5[0]).where(t.c2 < 10).order_by(t.c2).collect())
67
- check_view(t, v)
68
-
69
- # check view md after reload
70
- cl = pxt.Client(reload=True)
71
- t = cl.get_table('test_tbl')
72
- v = cl.get_table('test_view')
73
- check_view(t, v)
74
-
75
- view_query = v.select(v.v1).order_by(v.c2)
76
- base_query = t.select(t.c3 * 2.0).where(t.c2 < 10).order_by(t.c2)
77
-
78
- # insert data: of 20 new rows, only 10 are reflected in the view
79
- rows = list(t.select(t.c1, t.c1n, t.c2, t.c3, t.c4, t.c5, t.c6, t.c7, t.c10).where(t.c2 < 20).collect())
80
- status = t.insert(rows)
81
- assert status.num_rows == 30
82
- assert t.count() == 120
83
- check_view(t, v)
84
-
85
- # update data: cascade to view
86
- status = t.update({'c4': True, 'c3': t.c3 + 1.0, 'c10': t.c10 - 1.0}, where=t.c2 < 5, cascade=True)
87
- assert status.num_rows == 10 * 2 # *2: rows affected in both base table and view
88
- assert t.count() == 120
89
- check_view(t, v)
90
-
91
- # base table delete is reflected in view
92
- status = t.delete(where=t.c2 < 5)
93
- status.num_rows == 10 * 2 # *2: rows affected in both base table and view
94
- assert t.count() == 110
95
- check_view(t, v)
96
-
97
- # test delete view
98
- cl.drop_table('test_view')
99
- with pytest.raises(excs.Error) as exc_info:
100
- _ = cl.get_table('test_view')
101
- assert 'No such path:' in str(exc_info.value)
102
- cl = pxt.Client(reload=True)
103
- # still true after reload
104
- with pytest.raises(excs.Error) as exc_info:
105
- _ = cl.get_table('test_view')
106
- assert 'No such path:' in str(exc_info.value)
107
-
108
- t = cl.get_table('test_tbl')
109
- with pytest.raises(excs.Error) as exc_info:
110
- _ = cl.create_view('lambda_view', t, schema={'v1': lambda c3: c3 * 2.0})
111
- assert 'computed with a callable' in str(exc_info.value).lower()
112
-
113
- def test_parallel_views(self, test_client: pxt.Client) -> None:
114
- """Two views over the same base table, with non-overlapping filters"""
115
- cl = test_client
116
- t = self.create_tbl(cl)
117
-
118
- # create view with filter and computed columns
119
- v1 = cl.create_view('v1', t, schema={'v1': t.c3 * 2}, filter=t.c2 < 10)
120
- # create another view with a non-overlapping filter and computed columns
121
- v2 = cl.create_view('v2', t, schema={'v1': t.c3 * 3}, filter=(t.c2 < 20) & (t.c2 >= 10))
122
-
123
- # sanity checks
124
- v1_query = v1.select(v1.v1).order_by(v1.c2)
125
- v2_query = v2.select(v2.v1).order_by(v2.c2)
126
- b1_query = t.select(t.c3 * 2).where(t.c2 < 10).order_by(t.c2)
127
- b2_query = t.select(t.c3 * 3).where((t.c2 >= 10) & (t.c2 < 20)).order_by(t.c2)
128
- assert_resultset_eq(v1_query.collect(), b1_query.collect())
129
- assert_resultset_eq(v2_query.collect(), b2_query.collect())
130
-
131
- # insert data: of 20 new rows, only 10 show up in each view
132
- rows = list(t.select(t.c1, t.c1n, t.c2, t.c3, t.c4, t.c5, t.c6, t.c7, t.c10).where(t.c2 < 20).collect())
133
- status = t.insert(rows)
134
- assert status.num_rows == 40
135
- assert t.count() == 120
136
- assert v1.count() == 20
137
- assert v2.count() == 20
138
- assert_resultset_eq(v1_query.collect(), b1_query.collect())
139
- assert_resultset_eq(v2_query.collect(), b2_query.collect())
140
-
141
- # update data: cascade to views
142
- status = t.update(
143
- {'c4': True, 'c3': t.c3 + 1, 'c10': t.c10 - 1.0}, where=(t.c2 >= 5) & (t.c2 < 15), cascade=True)
144
- assert status.num_rows == 20 * 2 # *2: rows affected in both base table and view
145
- assert t.count() == 120
146
- assert v1.count() == 20
147
- assert v2.count() == 20
148
- assert_resultset_eq(v1_query.collect(), b1_query.collect())
149
- assert_resultset_eq(v2_query.collect(), b2_query.collect())
150
-
151
-
152
- # base table delete is reflected in view
153
- status = t.delete(where=(t.c2 >= 5) & (t.c2 < 15))
154
- status.num_rows == 10 * 2 # *2: rows affected in both base table and view
155
- assert t.count() == 100
156
- assert v1.count() == 10
157
- assert v2.count() == 10
158
- assert_resultset_eq(v1_query.collect(), b1_query.collect())
159
- assert_resultset_eq(v2_query.collect(), b2_query.collect())
160
-
161
- def test_chained_views(self, test_client: pxt.Client) -> None:
162
- """Two views, the second one is a view over the first one"""
163
- cl = test_client
164
- t = self.create_tbl(cl)
165
-
166
- # create view with filter and computed columns
167
- v1 = cl.create_view('v1', t, schema={'col1': t.c3 * 2}, filter=t.c2 < 10)
168
- # create a view on top of v1
169
- v2_schema = {
170
- 'col2': t.c3 * 3, # only base
171
- 'col3': v1.col1 / 2, # only v1
172
- 'col4': t.c10 + v1.col1, # both base and v1
173
- }
174
- v2 = cl.create_view('v2', v1, schema=v2_schema, filter=t.c2 < 5)
175
-
176
- def check_views():
177
- assert_resultset_eq(
178
- v1.select(v1.col1).order_by(v1.c2).collect(),
179
- t.select(t.c3 * 2).where(t.c2 < 10).order_by(t.c2).collect())
180
- assert_resultset_eq(
181
- v2.select(v2.col1).order_by(v2.c2).collect(),
182
- v1.select(v1.col1).where(v1.c2 < 5).order_by(v1.c2).collect())
183
- assert_resultset_eq(
184
- v2.select(v2.col2).order_by(v2.c2).collect(),
185
- t.select(t.c3 * 3).where(t.c2 < 5).order_by(t.c2).collect())
186
- assert_resultset_eq(
187
- v2.select(v2.col3).order_by(v2.c2).collect(),
188
- v1.select(v1.col1 / 2).where(v1.c2 < 5).order_by(v2.c2).collect())
189
- assert_resultset_eq(
190
- v2.select(v2.col4).order_by(v2.c2).collect(),
191
- v1.select(v1.c10 + v1.col1).where(v1.c2 < 5).order_by(v1.c2).collect())
192
- #t.select(t.c10 * 2).where(t.c2 < 5).order_by(t.c2).collect())
193
- check_views()
194
-
195
- # insert data: of 20 new rows; 10 show up in v1, 5 in v2
196
- base_version, v1_version, v2_version = t.version(), v1.version(), v2.version()
197
- rows = list(t.select(t.c1, t.c1n, t.c2, t.c3, t.c4, t.c5, t.c6, t.c7, t.c10).where(t.c2 < 20).collect())
198
- status = t.insert(rows)
199
- assert status.num_rows == 20 + 10 + 5
200
- assert t.count() == 120
201
- assert v1.count() == 20
202
- assert v2.count() == 10
203
- # all versions were incremented
204
- assert t.version() == base_version + 1
205
- assert v1.version() == v1_version + 1
206
- assert v2.version() == v2_version + 1
207
- check_views()
208
-
209
- # update data: cascade to both views
210
- base_version, v1_version, v2_version = t.version(), v1.version(), v2.version()
211
- status = t.update({'c4': True, 'c3': t.c3 + 1}, where=t.c2 < 15, cascade=True)
212
- assert status.num_rows == 30 + 20 + 10
213
- assert t.count() == 120
214
- # all versions were incremented
215
- assert t.version() == base_version + 1
216
- assert v1.version() == v1_version + 1
217
- assert v2.version() == v2_version + 1
218
- check_views()
219
-
220
- # update data: cascade only to v2
221
- base_version, v1_version, v2_version = t.version(), v1.version(), v2.version()
222
- status = t.update({'c10': t.c10 - 1.0}, where=t.c2 < 15, cascade=True)
223
- assert status.num_rows == 30 + 10
224
- assert t.count() == 120
225
- # v1 did not get updated
226
- assert t.version() == base_version + 1
227
- assert v1.version() == v1_version
228
- assert v2.version() == v2_version + 1
229
- check_views()
230
-
231
- # base table delete is reflected in both views
232
- base_version, v1_version, v2_version = t.version(), v1.version(), v2.version()
233
- status = t.delete(where=t.c2 == 0)
234
- status.num_rows == 1 + 1 + 1
235
- assert t.count() == 118
236
- assert v1.count() == 18
237
- assert v2.count() == 8
238
- # all versions were incremented
239
- assert t.version() == base_version + 1
240
- assert v1.version() == v1_version + 1
241
- assert v2.version() == v2_version + 1
242
- check_views()
243
-
244
- # base table delete is reflected only in v1
245
- base_version, v1_version, v2_version = t.version(), v1.version(), v2.version()
246
- status = t.delete(where=t.c2 == 5)
247
- status.num_rows == 1 + 1
248
- assert t.count() == 116
249
- assert v1.count() == 16
250
- assert v2.count() == 8
251
- # v2 was not updated
252
- assert t.version() == base_version + 1
253
- assert v1.version() == v1_version + 1
254
- assert v2.version() == v2_version
255
- check_views()
256
-
257
- def test_unstored_columns(self, test_client: pxt.Client) -> None:
258
- """Test chained views with unstored columns"""
259
- # create table with image column and two updateable int columns
260
- cl = test_client
261
- schema = {
262
- 'img': ImageType(),
263
- 'int1': IntType(nullable=False),
264
- 'int2': IntType(nullable=False),
265
- }
266
- t = cl.create_table('test_tbl', schema)
267
- # populate table with images of a defined size
268
- width, height = 100, 100
269
- rows = [
270
- {
271
- 'img': PIL.Image.new('RGB', (width, height), color=(0, 0, 0)).tobytes('jpeg', 'RGB'),
272
- 'int1': i,
273
- 'int2': i,
274
- }
275
- for i in range(100)
276
- ]
277
- t.insert(rows)
278
-
279
- # view with unstored column that depends on int1 and a manually updated column (int4)
280
- v1_schema = {
281
- 'img2': {
282
- 'value': t.img.crop([t.int1, t.int1, width, height]),
283
- 'stored': False,
284
- },
285
- 'int3': t.int1 * 2,
286
- 'int4': IntType(nullable=True), # TODO: add default
287
- }
288
- logger.debug('******************* CREATE V1')
289
- v1 = cl.create_view('v1', t, schema=v1_schema)
290
- v1.update({'int4': 1})
291
- _ = v1.select(v1.img2.width, v1.img2.height).collect()
292
-
293
- # view with stored column that depends on t and view1
294
- v2_schema = {
295
- 'img3': {
296
- # use the actual width and height of the image (not 100, which will pad the image)
297
- 'value': v1.img2.crop([t.int1 + t.int2, v1.int3 + v1.int4, v1.img2.width, v1.img2.height]),
298
- 'stored': True,
299
- },
300
- }
301
- logger.debug('******************* CREATE V2')
302
- v2 = cl.create_view('v2', v1, schema=v2_schema, filter=v1.int1 < 10)
303
-
304
- def check_views() -> None:
305
- assert_resultset_eq(
306
- v1.select(v1.img2.width, v1.img2.height).order_by(v1.int1).collect(),
307
- t.select(t.img.width - t.int1, t.img.height - t.int1).order_by(t.int1).collect())
308
- assert_resultset_eq(
309
- v2.select(v2.img3.width, v2.img3.height).order_by(v2.int1).collect(),
310
- v1.select(v1.img2.width - v1.int1 - v1.int2, v1.img2.height - v1.int3 - v1.int4)\
311
- .where(v1.int1 < 10).order_by(v1.int1).collect())
312
- check_views()
313
-
314
- logger.debug('******************* INSERT')
315
- status = t.insert(rows, fail_on_exception=False)
316
- v1.update({'int4': 1}, where=v1.int4 == None)
317
- logger.debug('******************* POST INSERT')
318
- check_views()
319
-
320
- # update int1:
321
- # - cascades to v1 and v2
322
- # - removes a row from v2 (only 9 rows in t now qualify)
323
- logger.debug('******************* UPDATE INT1')
324
- t.update({'int1': t.int1 + 1})
325
- logger.debug('******************* POST UPDATE INT1')
326
- check_views()
327
-
328
- # update int2:
329
- # - cascades only to v2
330
- # - but requires join against v1 to access int4
331
- # TODO: but requires join against v1 to access int3 and int4
332
- logger.debug('******************* UPDATE INT2')
333
- t.update({'int2': t.int2 + 1})
334
- logger.debug('******************* POST UPDATE INT2')
335
- check_views()
336
-
337
- def test_computed_cols(self, test_client: pxt.Client) -> None:
338
- cl = test_client
339
- t = self.create_tbl(cl)
340
-
341
- # create view with computed columns
342
- schema = {
343
- 'v1': t.c3 * 2.0,
344
- 'v2': t.c6.f5,
345
- }
346
- v = cl.create_view('test_view', t, schema=schema)
347
- assert_resultset_eq(
348
- v.select(v.v1).order_by(v.c2).show(0),
349
- t.select(t.c3 * 2.0).order_by(t.c2).show(0))
350
- # computed columns that don't reference the base table
351
- v.add_column(v3=v.v1 * 2.0)
352
- v.add_column(v4=v.v2[0])
353
-
354
- # use view md after reload
355
- cl = pxt.Client(reload=True)
356
- t = cl.get_table('test_tbl')
357
- v = cl.get_table('test_view')
358
-
359
- # insert data
360
- rows = list(t.select(t.c1, t.c1n, t.c2, t.c3, t.c4, t.c5, t.c6, t.c7, t.c10).collect())
361
- t.insert(rows)
362
- assert t.count() == 200
363
- assert_resultset_eq(
364
- v.select(v.v1).order_by(v.c2).show(0),
365
- t.select(t.c3 * 2.0).order_by(t.c2).show(0))
366
-
367
- # update data: cascade to view
368
- t.update({'c4': True, 'c3': t.c3 + 1.0, 'c10': t.c10 - 1.0}, where=t.c2 < 5, cascade=True)
369
- assert t.count() == 200
370
- assert_resultset_eq(
371
- v.select(v.v1).order_by(v.c2).show(0),
372
- t.select(t.c3 * 2.0).order_by(t.c2).show(0))
373
-
374
- # base table delete is reflected in view
375
- t.delete(where=t.c2 < 5)
376
- assert t.count() == 190
377
- assert_resultset_eq(
378
- v.select(v.v1).order_by(v.c2).show(0),
379
- t.select(t.c3 * 2.0).order_by(t.c2).show(0))
380
-
381
- def test_filter(self, test_client: pxt.Client) -> None:
382
- cl = test_client
383
- t = create_test_tbl(cl)
384
-
385
- # create view with filter
386
- v = cl.create_view('test_view', t, filter=t.c2 < 10)
387
- assert_resultset_eq(
388
- v.order_by(v.c2).show(0),
389
- t.where(t.c2 < 10).order_by(t.c2).show(0))
390
-
391
- # use view md after reload
392
- cl = pxt.Client(reload=True)
393
- t = cl.get_table('test_tbl')
394
- v = cl.get_table('test_view')
395
-
396
- # insert data: of 20 new rows, only 10 are reflected in the view
397
- rows = list(t.select(t.c1, t.c1n, t.c2, t.c3, t.c4, t.c5, t.c6, t.c7).where(t.c2 < 20).collect())
398
- t.insert(rows)
399
- assert t.count() == 120
400
- assert_resultset_eq(
401
- v.order_by(v.c2).show(0),
402
- t.where(t.c2 < 10).order_by(t.c2).show(0))
403
-
404
- # update data
405
- t.update({'c4': True, 'c3': t.c3 + 1.0}, where=t.c2 < 5, cascade=True)
406
- assert t.count() == 120
407
- assert_resultset_eq(
408
- v.order_by(v.c2).show(0),
409
- t.where(t.c2 < 10).order_by(t.c2).show(0))
410
-
411
- # base table delete is reflected in view
412
- t.delete(where=t.c2 < 5)
413
- assert t.count() == 110
414
- assert_resultset_eq(
415
- v.order_by(v.c2).show(0),
416
- t.where(t.c2 < 10).order_by(t.c2).show(0))
417
-
418
- # create views with filters containing date and datetime
419
- _ = cl.create_view('test_view_2', t, filter=t.c5 >= datetime.date.today())
420
- _ = cl.create_view('test_view_3', t, filter=t.c5 < datetime.datetime.now())
421
-
422
- def test_view_of_snapshot(self, test_client: pxt.Client) -> None:
423
- """Test view over a snapshot"""
424
- cl = test_client
425
- t = self.create_tbl(cl)
426
- snap = cl.create_view('test_snap', t, is_snapshot=True)
427
-
428
- # create view with filter and computed columns
429
- schema = {
430
- 'v1': snap.c3 * 2.0,
431
- 'v2': snap.c6.f5,
432
- }
433
- v = cl.create_view('test_view', snap, schema=schema, filter=snap.c2 < 10)
434
-
435
- def check_view(s: pxt.Table, v: pxt.Table) -> None:
436
- assert v.count() == s.where(s.c2 < 10).count()
437
- assert_resultset_eq(
438
- v.select(v.v1).order_by(v.c2).collect(),
439
- s.select(s.c3 * 2.0).where(s.c2 < 10).order_by(s.c2).collect())
440
- assert_resultset_eq(
441
- v.select(v.v2).order_by(v.c2).collect(),
442
- s.select(s.c6.f5).where(s.c2 < 10).order_by(s.c2).collect())
443
-
444
- check_view(snap, v)
445
- # computed columns that don't reference the base table
446
- v.add_column(v3=v.v1 * 2.0)
447
- v.add_column(v4=v.v2[0])
448
- assert v.count() == t.where(t.c2 < 10).count()
449
-
450
- # use view md after reload
451
- cl = pxt.Client(reload=True)
452
- t = cl.get_table('test_tbl')
453
- snap = cl.get_table('test_snap')
454
- v = cl.get_table('test_view')
455
-
456
- # insert data: no changes to view
457
- rows = list(t.select(t.c1, t.c1n, t.c2, t.c3, t.c4, t.c5, t.c6, t.c7, t.c10).where(t.c2 < 20).collect())
458
- t.insert(rows)
459
- assert t.count() == 120
460
- check_view(snap, v)
461
-
462
- # update data: no changes to view
463
- t.update({'c4': True, 'c3': t.c3 + 1.0, 'c10': t.c10 - 1.0}, where=t.c2 < 5, cascade=True)
464
- assert t.count() == 120
465
- check_view(snap, v)
466
-
467
- # base table delete: no change to view
468
- t.delete(where=t.c2 < 5)
469
- assert t.count() == 110
470
- check_view(snap, v)
471
-
472
- def test_snapshots(self, test_client: pxt.Client) -> None:
473
- """Test snapshot of a view of a snapshot"""
474
- cl = test_client
475
- t = self.create_tbl(cl)
476
- s = cl.create_view('test_snap', t, is_snapshot=True)
477
- assert s.select(s.c2).order_by(s.c2).collect()['c2'] == t.select(t.c2).order_by(t.c2).collect()['c2']
478
-
479
- with pytest.raises(excs.Error) as exc_info:
480
- v = cl.create_view('test_view', s, schema={'v1': t.c3 * 2.0})
481
- assert 'value expression cannot be computed in the context of the base test_snap' in str(exc_info.value)
482
-
483
- with pytest.raises(excs.Error) as exc_info:
484
- v = cl.create_view('test_view', s, filter=t.c2 < 10)
485
- assert 'filter cannot be computed in the context of the base test_snap' in str(exc_info.value).lower()
486
-
487
- # create view with filter and computed columns
488
- schema = {
489
- 'v1': s.c3 * 2.0,
490
- 'v2': s.c6.f5,
491
- }
492
- v = cl.create_view('test_view', s, schema=schema, filter=s.c2 < 10)
493
- orig_view_cols = v.column_names()
494
- view_s = cl.create_view('test_view_snap', v, is_snapshot=True)
495
- assert set(view_s.column_names()) == set(orig_view_cols)
496
-
497
- def check(s1: pxt.Table, v: pxt.Table, s2: pxt.Table) -> None:
498
- assert s1.where(s1.c2 < 10).count() == v.count()
499
- assert v.count() == s2.count()
500
- assert_resultset_eq(
501
- s1.select(s1.c3 * 2.0, s1.c6.f5).where(s1.c2 < 10).order_by(s1.c2).collect(),
502
- v.select(v.v1, v.v2).order_by(v.c2).collect())
503
- assert_resultset_eq(
504
- v.select(v.c3, v.c6, v.v1, v.v2).order_by(v.c2).collect(),
505
- s2.select(s2.c3, s2.c6, s2.v1, s2.v2).order_by(s2.c2).collect())
506
- check(s, v, view_s)
507
-
508
- # add more columns
509
- v.add_column(v3=v.v1 * 2.0)
510
- v.add_column(v4=v.v2[0])
511
- check(s, v, view_s)
512
- assert set(view_s.column_names()) == set(orig_view_cols)
513
-
514
- # check md after reload
515
- cl = pxt.Client(reload=True)
516
- t = cl.get_table('test_tbl')
517
- view_s = cl.get_table('test_view_snap')
518
- check(s, v, view_s)
519
- assert set(view_s.column_names()) == set(orig_view_cols)
520
-
521
- # insert data: no changes to snapshot
522
- rows = list(t.select(t.c1, t.c1n, t.c2, t.c3, t.c4, t.c5, t.c6, t.c7, t.c10).where(t.c2 < 20).collect())
523
- t.insert(rows)
524
- assert t.count() == 120
525
- check(s, v, view_s)
526
-
527
- # update data: no changes to snapshot
528
- t.update({'c4': True, 'c3': t.c3 + 1.0, 'c10': t.c10 - 1.0}, where=t.c2 < 5, cascade=True)
529
- assert t.count() == 120
530
- check(s, v, view_s)
531
-
532
- # base table delete: no changes to snapshot
533
- t.delete(where=t.c2 < 5)
534
- assert t.count() == 110
535
- check(s, v, view_s)