pixeltable 0.4.3__py3-none-any.whl → 0.4.5__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.
- pixeltable/__version__.py +2 -2
- pixeltable/catalog/__init__.py +1 -1
- pixeltable/catalog/catalog.py +619 -255
- pixeltable/catalog/dir.py +1 -2
- pixeltable/catalog/insertable_table.py +9 -9
- pixeltable/catalog/path.py +59 -20
- pixeltable/catalog/schema_object.py +10 -4
- pixeltable/catalog/table.py +51 -53
- pixeltable/catalog/table_version.py +216 -156
- pixeltable/catalog/table_version_path.py +1 -1
- pixeltable/catalog/tbl_ops.py +44 -0
- pixeltable/catalog/view.py +63 -65
- pixeltable/config.py +12 -4
- pixeltable/dataframe.py +75 -6
- pixeltable/env.py +46 -17
- pixeltable/exec/aggregation_node.py +1 -1
- pixeltable/exec/cache_prefetch_node.py +2 -6
- pixeltable/exec/component_iteration_node.py +4 -3
- pixeltable/exec/data_row_batch.py +10 -51
- pixeltable/exec/expr_eval/expr_eval_node.py +2 -2
- pixeltable/exec/in_memory_data_node.py +17 -16
- pixeltable/exec/sql_node.py +6 -7
- pixeltable/exprs/column_ref.py +2 -1
- pixeltable/exprs/data_row.py +13 -13
- pixeltable/exprs/row_builder.py +16 -4
- pixeltable/exprs/string_op.py +1 -1
- pixeltable/func/expr_template_function.py +1 -4
- pixeltable/functions/date.py +1 -1
- pixeltable/functions/gemini.py +4 -4
- pixeltable/functions/math.py +1 -1
- pixeltable/functions/openai.py +9 -6
- pixeltable/functions/timestamp.py +6 -6
- pixeltable/functions/video.py +2 -6
- pixeltable/globals.py +62 -33
- pixeltable/io/datarows.py +2 -1
- pixeltable/io/pandas.py +1 -0
- pixeltable/io/table_data_conduit.py +12 -13
- pixeltable/iterators/audio.py +17 -8
- pixeltable/iterators/image.py +5 -2
- pixeltable/metadata/schema.py +39 -2
- pixeltable/plan.py +5 -14
- pixeltable/share/packager.py +13 -13
- pixeltable/store.py +31 -7
- pixeltable/type_system.py +2 -1
- pixeltable/utils/filecache.py +1 -1
- pixeltable/utils/http_server.py +2 -3
- pixeltable/utils/media_store.py +90 -34
- {pixeltable-0.4.3.dist-info → pixeltable-0.4.5.dist-info}/METADATA +1 -1
- {pixeltable-0.4.3.dist-info → pixeltable-0.4.5.dist-info}/RECORD +52 -51
- {pixeltable-0.4.3.dist-info → pixeltable-0.4.5.dist-info}/LICENSE +0 -0
- {pixeltable-0.4.3.dist-info → pixeltable-0.4.5.dist-info}/WHEEL +0 -0
- {pixeltable-0.4.3.dist-info → pixeltable-0.4.5.dist-info}/entry_points.txt +0 -0
pixeltable/catalog/dir.py
CHANGED
|
@@ -54,8 +54,8 @@ class InsertableTable(Table):
|
|
|
54
54
|
super().__init__(tbl_version.id, dir_id, tbl_version.get().name, tbl_version_path)
|
|
55
55
|
self._tbl_version = tbl_version
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
def _display_name(self) -> str:
|
|
58
|
+
assert not self._tbl_version_path.is_replica()
|
|
59
59
|
return 'table'
|
|
60
60
|
|
|
61
61
|
@classmethod
|
|
@@ -75,10 +75,10 @@ class InsertableTable(Table):
|
|
|
75
75
|
column_names = [col.name for col in columns]
|
|
76
76
|
for pk_col in primary_key:
|
|
77
77
|
if pk_col not in column_names:
|
|
78
|
-
raise excs.Error(f'Primary key column {pk_col} not found in table schema')
|
|
78
|
+
raise excs.Error(f'Primary key column {pk_col!r} not found in table schema.')
|
|
79
79
|
col = columns[column_names.index(pk_col)]
|
|
80
80
|
if col.col_type.nullable:
|
|
81
|
-
raise excs.Error(f'Primary key column {pk_col} cannot be nullable')
|
|
81
|
+
raise excs.Error(f'Primary key column {pk_col!r} cannot be nullable.')
|
|
82
82
|
col.is_pk = True
|
|
83
83
|
|
|
84
84
|
_, tbl_version = TableVersion.create(
|
|
@@ -101,8 +101,8 @@ class InsertableTable(Table):
|
|
|
101
101
|
tbl_version.insert(None, df, fail_on_exception=True)
|
|
102
102
|
session.commit()
|
|
103
103
|
|
|
104
|
-
_logger.info(f'Created table
|
|
105
|
-
Env.get().console_logger.info(f'Created table
|
|
104
|
+
_logger.info(f'Created table {name!r}, id={tbl_version.id}')
|
|
105
|
+
Env.get().console_logger.info(f'Created table {name!r}.')
|
|
106
106
|
return tbl
|
|
107
107
|
|
|
108
108
|
def _get_metadata(self) -> dict[str, Any]:
|
|
@@ -204,9 +204,9 @@ class InsertableTable(Table):
|
|
|
204
204
|
|
|
205
205
|
for col_name, val in row.items():
|
|
206
206
|
if col_name not in valid_col_names:
|
|
207
|
-
raise excs.Error(f'Unknown column name {col_name} in row {row}')
|
|
207
|
+
raise excs.Error(f'Unknown column name {col_name!r} in row {row}')
|
|
208
208
|
if col_name in computed_col_names:
|
|
209
|
-
raise excs.Error(f'Value for computed column {col_name} in row {row}')
|
|
209
|
+
raise excs.Error(f'Value for computed column {col_name!r} in row {row}')
|
|
210
210
|
|
|
211
211
|
# validate data
|
|
212
212
|
col = self._tbl_version_path.get_column(col_name)
|
|
@@ -246,4 +246,4 @@ class InsertableTable(Table):
|
|
|
246
246
|
return []
|
|
247
247
|
|
|
248
248
|
def _table_descriptor(self) -> str:
|
|
249
|
-
return
|
|
249
|
+
return self._display_str()
|
pixeltable/catalog/path.py
CHANGED
|
@@ -1,20 +1,57 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Optional
|
|
5
5
|
|
|
6
6
|
from pixeltable import exceptions as excs
|
|
7
7
|
|
|
8
|
-
from .globals import
|
|
8
|
+
from .globals import is_valid_identifier
|
|
9
9
|
|
|
10
10
|
_logger = logging.getLogger('pixeltable')
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class Path:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
components: list[str]
|
|
15
|
+
version: Optional[int]
|
|
16
|
+
|
|
17
|
+
def __init__(self, components: list[str], version: Optional[int] = None) -> None:
|
|
18
|
+
assert len(components) > 0
|
|
19
|
+
self.components = components
|
|
20
|
+
self.version = version
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def parse(
|
|
24
|
+
cls,
|
|
25
|
+
path: str,
|
|
26
|
+
allow_empty_path: bool = False,
|
|
27
|
+
allow_system_path: bool = False,
|
|
28
|
+
allow_versioned_path: bool = False,
|
|
29
|
+
) -> Path:
|
|
30
|
+
components: list[str]
|
|
31
|
+
version: Optional[int]
|
|
32
|
+
if ':' in path:
|
|
33
|
+
parts = path.split(':')
|
|
34
|
+
if len(parts) != 2:
|
|
35
|
+
raise excs.Error(f'Invalid path: {path}')
|
|
36
|
+
try:
|
|
37
|
+
components = parts[0].split('.')
|
|
38
|
+
version = int(parts[1])
|
|
39
|
+
except ValueError:
|
|
40
|
+
raise excs.Error(f'Invalid path: {path}') from None
|
|
41
|
+
else:
|
|
42
|
+
components = path.split('.')
|
|
43
|
+
version = None
|
|
44
|
+
|
|
45
|
+
if components == [''] and not allow_empty_path:
|
|
46
|
+
raise excs.Error(f'Invalid path: {path}')
|
|
47
|
+
|
|
48
|
+
if components != [''] and not all(is_valid_identifier(c, allow_system_path) for c in components):
|
|
49
|
+
raise excs.Error(f'Invalid path: {path}')
|
|
50
|
+
|
|
51
|
+
if version is not None and not allow_versioned_path:
|
|
52
|
+
raise excs.Error(f'Versioned path not allowed here: {path}')
|
|
53
|
+
|
|
54
|
+
return Path(components, version)
|
|
18
55
|
|
|
19
56
|
@property
|
|
20
57
|
def len(self) -> int:
|
|
@@ -22,7 +59,6 @@ class Path:
|
|
|
22
59
|
|
|
23
60
|
@property
|
|
24
61
|
def name(self) -> str:
|
|
25
|
-
assert len(self.components) > 0
|
|
26
62
|
return self.components[-1]
|
|
27
63
|
|
|
28
64
|
@property
|
|
@@ -36,18 +72,15 @@ class Path:
|
|
|
36
72
|
@property
|
|
37
73
|
def parent(self) -> Path:
|
|
38
74
|
if len(self.components) == 1:
|
|
39
|
-
|
|
40
|
-
return self
|
|
41
|
-
else:
|
|
42
|
-
return Path('', empty_is_valid=True, allow_system_paths=True)
|
|
75
|
+
return ROOT_PATH # Includes the case of the root path, which is its own parent.
|
|
43
76
|
else:
|
|
44
|
-
return Path(
|
|
77
|
+
return Path(self.components[:-1])
|
|
45
78
|
|
|
46
79
|
def append(self, name: str) -> Path:
|
|
47
80
|
if self.is_root:
|
|
48
|
-
return Path(name
|
|
81
|
+
return Path([name])
|
|
49
82
|
else:
|
|
50
|
-
return Path(
|
|
83
|
+
return Path([*self.components, name])
|
|
51
84
|
|
|
52
85
|
def is_ancestor(self, other: Path, is_parent: bool = False) -> bool:
|
|
53
86
|
"""
|
|
@@ -60,22 +93,25 @@ class Path:
|
|
|
60
93
|
is_prefix = self.components == other.components[: self.len]
|
|
61
94
|
return is_prefix and (self.len == (other.len - 1) or not is_parent)
|
|
62
95
|
|
|
63
|
-
def ancestors(self) ->
|
|
96
|
+
def ancestors(self) -> list[Path]:
|
|
64
97
|
"""
|
|
65
|
-
Return all ancestors of this path in top-down order including root.
|
|
98
|
+
Return all proper ancestors of this path in top-down order including root.
|
|
66
99
|
If this path is for the root directory, which has no parent, then None is returned.
|
|
67
100
|
"""
|
|
68
101
|
if self.is_root:
|
|
69
|
-
return
|
|
102
|
+
return []
|
|
70
103
|
else:
|
|
71
|
-
for i in range(
|
|
72
|
-
yield Path('.'.join(self.components[0:i]), empty_is_valid=True)
|
|
104
|
+
return [Path(self.components[:i]) if i > 0 else ROOT_PATH for i in range(len(self.components))]
|
|
73
105
|
|
|
74
106
|
def __repr__(self) -> str:
|
|
75
107
|
return repr(str(self))
|
|
76
108
|
|
|
77
109
|
def __str__(self) -> str:
|
|
78
|
-
|
|
110
|
+
base = '.'.join(self.components)
|
|
111
|
+
if self.version is not None:
|
|
112
|
+
return f'{base}:{self.version}'
|
|
113
|
+
else:
|
|
114
|
+
return base
|
|
79
115
|
|
|
80
116
|
def __eq__(self, other: object) -> bool:
|
|
81
117
|
return isinstance(other, Path) and str(self) == str(other)
|
|
@@ -85,3 +121,6 @@ class Path:
|
|
|
85
121
|
|
|
86
122
|
def __lt__(self, other: Path) -> bool:
|
|
87
123
|
return str(self) < str(other)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
ROOT_PATH = Path([''])
|
|
@@ -18,6 +18,7 @@ class SchemaObject:
|
|
|
18
18
|
|
|
19
19
|
def __init__(self, obj_id: UUID, name: str, dir_id: Optional[UUID]):
|
|
20
20
|
# make these private so they don't collide with column names (id and name are fairly common)
|
|
21
|
+
assert dir_id is None or isinstance(dir_id, UUID), type(dir_id)
|
|
21
22
|
self._id = obj_id
|
|
22
23
|
self._name = name
|
|
23
24
|
self._dir_id = dir_id
|
|
@@ -42,22 +43,27 @@ class SchemaObject:
|
|
|
42
43
|
|
|
43
44
|
def get_metadata(self) -> dict[str, Any]:
|
|
44
45
|
"""Returns metadata associated with this schema object."""
|
|
45
|
-
from pixeltable.catalog import
|
|
46
|
+
from pixeltable.catalog import retry_loop
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
@retry_loop(for_write=False)
|
|
49
|
+
def op() -> dict[str, Any]:
|
|
48
50
|
return self._get_metadata()
|
|
49
51
|
|
|
52
|
+
return op()
|
|
53
|
+
|
|
50
54
|
def _get_metadata(self) -> dict[str, Any]:
|
|
51
55
|
return {'name': self._name, 'path': self._path()}
|
|
52
56
|
|
|
53
|
-
@classmethod
|
|
54
57
|
@abstractmethod
|
|
55
|
-
def _display_name(
|
|
58
|
+
def _display_name(self) -> str:
|
|
56
59
|
"""
|
|
57
60
|
Return name displayed in error messages.
|
|
58
61
|
"""
|
|
59
62
|
pass
|
|
60
63
|
|
|
64
|
+
def _display_str(self) -> str:
|
|
65
|
+
return f'{self._display_name()} {self._path()!r}'
|
|
66
|
+
|
|
61
67
|
def _move(self, new_name: str, new_dir_id: UUID) -> None:
|
|
62
68
|
"""Subclasses need to override this to make the change persistent"""
|
|
63
69
|
self._name = new_name
|
pixeltable/catalog/table.py
CHANGED
|
@@ -89,6 +89,8 @@ class Table(SchemaObject):
|
|
|
89
89
|
|
|
90
90
|
```python
|
|
91
91
|
{
|
|
92
|
+
'name': 'my_table',
|
|
93
|
+
'path': 'my_dir.my_subdir.my_table',
|
|
92
94
|
'base': None, # If this is a view or snapshot, will contain the name of its base table
|
|
93
95
|
'schema': {
|
|
94
96
|
'col1': StringType(),
|
|
@@ -96,6 +98,7 @@ class Table(SchemaObject):
|
|
|
96
98
|
},
|
|
97
99
|
'is_replica': False,
|
|
98
100
|
'version': 22,
|
|
101
|
+
'version_created': datetime.datetime(...),
|
|
99
102
|
'schema_version': 1,
|
|
100
103
|
'comment': '',
|
|
101
104
|
'num_retained_versions': 10,
|
|
@@ -112,6 +115,9 @@ class Table(SchemaObject):
|
|
|
112
115
|
md['schema'] = self._get_schema()
|
|
113
116
|
md['is_replica'] = self._tbl_version_path.is_replica()
|
|
114
117
|
md['version'] = self._get_version()
|
|
118
|
+
md['version_created'] = datetime.datetime.fromtimestamp(
|
|
119
|
+
self._tbl_version_path.tbl_version.get().created_at, tz=datetime.timezone.utc
|
|
120
|
+
)
|
|
115
121
|
md['schema_version'] = self._tbl_version_path.schema_version()
|
|
116
122
|
md['comment'] = self._get_comment()
|
|
117
123
|
md['num_retained_versions'] = self._get_num_retained_versions()
|
|
@@ -147,11 +153,15 @@ class Table(SchemaObject):
|
|
|
147
153
|
Returns:
|
|
148
154
|
A list of view paths.
|
|
149
155
|
"""
|
|
150
|
-
from pixeltable.catalog import
|
|
156
|
+
from pixeltable.catalog import retry_loop
|
|
151
157
|
|
|
152
|
-
|
|
158
|
+
# we need retry_loop() here, because we end up loading Tables for the views
|
|
159
|
+
@retry_loop(tbl=self._tbl_version_path, for_write=False)
|
|
160
|
+
def op() -> list[str]:
|
|
153
161
|
return [t._path() for t in self._get_views(recursive=recursive)]
|
|
154
162
|
|
|
163
|
+
return op()
|
|
164
|
+
|
|
155
165
|
def _get_views(self, *, recursive: bool = True, include_snapshots: bool = True) -> list['Table']:
|
|
156
166
|
cat = catalog.Catalog.get()
|
|
157
167
|
view_ids = cat.get_view_ids(self._id)
|
|
@@ -178,7 +188,7 @@ class Table(SchemaObject):
|
|
|
178
188
|
"""
|
|
179
189
|
from pixeltable.catalog import Catalog
|
|
180
190
|
|
|
181
|
-
with Catalog.get().begin_xact(for_write=False):
|
|
191
|
+
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=False):
|
|
182
192
|
return self._df().select(*items, **named_items)
|
|
183
193
|
|
|
184
194
|
def where(self, pred: 'exprs.Expr') -> 'pxt.DataFrame':
|
|
@@ -188,7 +198,7 @@ class Table(SchemaObject):
|
|
|
188
198
|
"""
|
|
189
199
|
from pixeltable.catalog import Catalog
|
|
190
200
|
|
|
191
|
-
with Catalog.get().begin_xact(for_write=False):
|
|
201
|
+
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=False):
|
|
192
202
|
return self._df().where(pred)
|
|
193
203
|
|
|
194
204
|
def join(
|
|
@@ -201,7 +211,7 @@ class Table(SchemaObject):
|
|
|
201
211
|
"""Join this table with another table."""
|
|
202
212
|
from pixeltable.catalog import Catalog
|
|
203
213
|
|
|
204
|
-
with Catalog.get().begin_xact(for_write=False):
|
|
214
|
+
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=False):
|
|
205
215
|
return self._df().join(other, on=on, how=how)
|
|
206
216
|
|
|
207
217
|
def order_by(self, *items: 'exprs.Expr', asc: bool = True) -> 'pxt.DataFrame':
|
|
@@ -211,7 +221,7 @@ class Table(SchemaObject):
|
|
|
211
221
|
"""
|
|
212
222
|
from pixeltable.catalog import Catalog
|
|
213
223
|
|
|
214
|
-
with Catalog.get().begin_xact(for_write=False):
|
|
224
|
+
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=False):
|
|
215
225
|
return self._df().order_by(*items, asc=asc)
|
|
216
226
|
|
|
217
227
|
def group_by(self, *items: 'exprs.Expr') -> 'pxt.DataFrame':
|
|
@@ -221,7 +231,7 @@ class Table(SchemaObject):
|
|
|
221
231
|
"""
|
|
222
232
|
from pixeltable.catalog import Catalog
|
|
223
233
|
|
|
224
|
-
with Catalog.get().begin_xact(for_write=False):
|
|
234
|
+
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=False):
|
|
225
235
|
return self._df().group_by(*items)
|
|
226
236
|
|
|
227
237
|
def distinct(self) -> 'pxt.DataFrame':
|
|
@@ -277,10 +287,7 @@ class Table(SchemaObject):
|
|
|
277
287
|
return {c.name: c.col_type for c in self._tbl_version_path.columns()}
|
|
278
288
|
|
|
279
289
|
def get_base_table(self) -> Optional['Table']:
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
with Catalog.get().begin_xact(for_write=False):
|
|
283
|
-
return self._get_base_table()
|
|
290
|
+
return self._get_base_table()
|
|
284
291
|
|
|
285
292
|
@abc.abstractmethod
|
|
286
293
|
def _get_base_table(self) -> Optional['Table']:
|
|
@@ -321,7 +328,7 @@ class Table(SchemaObject):
|
|
|
321
328
|
"""
|
|
322
329
|
from pixeltable.catalog import Catalog
|
|
323
330
|
|
|
324
|
-
with Catalog.get().begin_xact(for_write=False):
|
|
331
|
+
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=False):
|
|
325
332
|
helper = DescriptionHelper()
|
|
326
333
|
helper.append(self._table_descriptor())
|
|
327
334
|
helper.append(self._col_descriptor())
|
|
@@ -492,8 +499,7 @@ class Table(SchemaObject):
|
|
|
492
499
|
|
|
493
500
|
# lock_mutable_tree=True: we might end up having to drop existing columns, which requires locking the tree
|
|
494
501
|
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=True, lock_mutable_tree=True):
|
|
495
|
-
|
|
496
|
-
raise excs.Error('Cannot add column to a snapshot.')
|
|
502
|
+
self.__check_mutable('add columns to')
|
|
497
503
|
col_schema = {
|
|
498
504
|
col_name: {'type': ts.ColumnType.normalize_type(spec, nullable_default=True, allow_builtin_types=False)}
|
|
499
505
|
for col_name, spec in schema.items()
|
|
@@ -553,24 +559,18 @@ class Table(SchemaObject):
|
|
|
553
559
|
|
|
554
560
|
>>> tbl.add_columns({'new_col': pxt.Int})
|
|
555
561
|
"""
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
col_type = next(iter(kwargs.values()))
|
|
569
|
-
if not isinstance(col_type, (ts.ColumnType, type, _GenericAlias)):
|
|
570
|
-
raise excs.Error(
|
|
571
|
-
'The argument to add_column() must be a type; did you intend to use add_computed_column() instead?'
|
|
572
|
-
)
|
|
573
|
-
return self.add_columns(kwargs, if_exists=if_exists)
|
|
562
|
+
# verify kwargs and construct column schema dict
|
|
563
|
+
if len(kwargs) != 1:
|
|
564
|
+
raise excs.Error(
|
|
565
|
+
f'add_column() requires exactly one keyword argument of the form "col_name=col_type"; '
|
|
566
|
+
f'got {len(kwargs)} instead ({", ".join(kwargs.keys())})'
|
|
567
|
+
)
|
|
568
|
+
col_type = next(iter(kwargs.values()))
|
|
569
|
+
if not isinstance(col_type, (ts.ColumnType, type, _GenericAlias)):
|
|
570
|
+
raise excs.Error(
|
|
571
|
+
'The argument to add_column() must be a type; did you intend to use add_computed_column() instead?'
|
|
572
|
+
)
|
|
573
|
+
return self.add_columns(kwargs, if_exists=if_exists)
|
|
574
574
|
|
|
575
575
|
def add_computed_column(
|
|
576
576
|
self,
|
|
@@ -622,8 +622,7 @@ class Table(SchemaObject):
|
|
|
622
622
|
from pixeltable.catalog import Catalog
|
|
623
623
|
|
|
624
624
|
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=True, lock_mutable_tree=True):
|
|
625
|
-
|
|
626
|
-
raise excs.Error('Cannot add column to a snapshot.')
|
|
625
|
+
self.__check_mutable('add columns to')
|
|
627
626
|
if len(kwargs) != 1:
|
|
628
627
|
raise excs.Error(
|
|
629
628
|
f'add_computed_column() requires exactly one keyword argument of the form '
|
|
@@ -808,10 +807,10 @@ class Table(SchemaObject):
|
|
|
808
807
|
from pixeltable.catalog import Catalog
|
|
809
808
|
|
|
810
809
|
cat = Catalog.get()
|
|
810
|
+
|
|
811
811
|
# lock_mutable_tree=True: we need to be able to see whether any transitive view has column dependents
|
|
812
812
|
with cat.begin_xact(tbl=self._tbl_version_path, for_write=True, lock_mutable_tree=True):
|
|
813
|
-
|
|
814
|
-
raise excs.Error('Cannot drop column from a snapshot.')
|
|
813
|
+
self.__check_mutable('drop columns from')
|
|
815
814
|
col: Column = None
|
|
816
815
|
if_not_exists_ = IfNotExistsParam.validated(if_not_exists, 'if_not_exists')
|
|
817
816
|
|
|
@@ -835,7 +834,7 @@ class Table(SchemaObject):
|
|
|
835
834
|
dependent_user_cols = [c for c in cat.get_column_dependents(col.tbl.id, col.id) if c.name is not None]
|
|
836
835
|
if len(dependent_user_cols) > 0:
|
|
837
836
|
raise excs.Error(
|
|
838
|
-
f'Cannot drop column
|
|
837
|
+
f'Cannot drop column {col.name!r} because the following columns depend on it:\n'
|
|
839
838
|
f'{", ".join(c.name for c in dependent_user_cols)}'
|
|
840
839
|
)
|
|
841
840
|
|
|
@@ -989,8 +988,7 @@ class Table(SchemaObject):
|
|
|
989
988
|
from pixeltable.catalog import Catalog
|
|
990
989
|
|
|
991
990
|
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=True, lock_mutable_tree=True):
|
|
992
|
-
|
|
993
|
-
raise excs.Error('Cannot add an index to a snapshot')
|
|
991
|
+
self.__check_mutable('add an index to')
|
|
994
992
|
col = self._resolve_column_parameter(column)
|
|
995
993
|
|
|
996
994
|
if idx_name is not None and idx_name in self._tbl_version.get().idxs_by_name:
|
|
@@ -1174,8 +1172,7 @@ class Table(SchemaObject):
|
|
|
1174
1172
|
) -> None:
|
|
1175
1173
|
from pixeltable.catalog import Catalog
|
|
1176
1174
|
|
|
1177
|
-
|
|
1178
|
-
raise excs.Error('Cannot drop an index from a snapshot')
|
|
1175
|
+
self.__check_mutable('drop an index from')
|
|
1179
1176
|
assert (col is None) != (idx_name is None)
|
|
1180
1177
|
|
|
1181
1178
|
if idx_name is not None:
|
|
@@ -1347,8 +1344,7 @@ class Table(SchemaObject):
|
|
|
1347
1344
|
from pixeltable.catalog import Catalog
|
|
1348
1345
|
|
|
1349
1346
|
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=True, lock_mutable_tree=True):
|
|
1350
|
-
|
|
1351
|
-
raise excs.Error('Cannot update a snapshot')
|
|
1347
|
+
self.__check_mutable('update')
|
|
1352
1348
|
result = self._tbl_version.get().update(value_spec, where, cascade)
|
|
1353
1349
|
FileCache.get().emit_eviction_warnings()
|
|
1354
1350
|
return result
|
|
@@ -1387,8 +1383,7 @@ class Table(SchemaObject):
|
|
|
1387
1383
|
from pixeltable.catalog import Catalog
|
|
1388
1384
|
|
|
1389
1385
|
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=True, lock_mutable_tree=True):
|
|
1390
|
-
|
|
1391
|
-
raise excs.Error('Cannot update a snapshot')
|
|
1386
|
+
self.__check_mutable('update')
|
|
1392
1387
|
rows = list(rows)
|
|
1393
1388
|
|
|
1394
1389
|
row_updates: list[dict[Column, exprs.Expr]] = []
|
|
@@ -1456,8 +1451,7 @@ class Table(SchemaObject):
|
|
|
1456
1451
|
cat = Catalog.get()
|
|
1457
1452
|
# lock_mutable_tree=True: we need to be able to see whether any transitive view has column dependents
|
|
1458
1453
|
with cat.begin_xact(tbl=self._tbl_version_path, for_write=True, lock_mutable_tree=True):
|
|
1459
|
-
|
|
1460
|
-
raise excs.Error('Cannot recompute columns of a snapshot.')
|
|
1454
|
+
self.__check_mutable('recompute columns of')
|
|
1461
1455
|
if len(columns) == 0:
|
|
1462
1456
|
raise excs.Error('At least one column must be specified to recompute')
|
|
1463
1457
|
if errors_only and len(columns) > 1:
|
|
@@ -1514,8 +1508,7 @@ class Table(SchemaObject):
|
|
|
1514
1508
|
from pixeltable.catalog import Catalog
|
|
1515
1509
|
|
|
1516
1510
|
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=True, lock_mutable_tree=True):
|
|
1517
|
-
|
|
1518
|
-
raise excs.Error('Cannot revert a snapshot')
|
|
1511
|
+
self.__check_mutable('revert')
|
|
1519
1512
|
self._tbl_version.get().revert()
|
|
1520
1513
|
# remove cached md in order to force a reload on the next operation
|
|
1521
1514
|
self._tbl_version_path.clear_cached_md()
|
|
@@ -1530,8 +1523,7 @@ class Table(SchemaObject):
|
|
|
1530
1523
|
from pixeltable.catalog import Catalog
|
|
1531
1524
|
|
|
1532
1525
|
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=True, lock_mutable_tree=False):
|
|
1533
|
-
|
|
1534
|
-
raise excs.Error(f'Table `{self._name}` is a snapshot, so it cannot be linked to an external store.')
|
|
1526
|
+
self.__check_mutable('link an external store to')
|
|
1535
1527
|
if store.name in self.external_stores():
|
|
1536
1528
|
raise excs.Error(f'Table `{self._name}` already has an external store with that name: {store.name}')
|
|
1537
1529
|
_logger.info(f'Linking external store `{store.name}` to table `{self._name}`')
|
|
@@ -1560,7 +1552,7 @@ class Table(SchemaObject):
|
|
|
1560
1552
|
"""
|
|
1561
1553
|
from pixeltable.catalog import Catalog
|
|
1562
1554
|
|
|
1563
|
-
if self._tbl_version_path.
|
|
1555
|
+
if not self._tbl_version_path.is_mutable():
|
|
1564
1556
|
return
|
|
1565
1557
|
with Catalog.get().begin_xact(tbl=self._tbl_version_path, for_write=True, lock_mutable_tree=False):
|
|
1566
1558
|
all_stores = self.external_stores()
|
|
@@ -1600,7 +1592,7 @@ class Table(SchemaObject):
|
|
|
1600
1592
|
"""
|
|
1601
1593
|
from pixeltable.catalog import Catalog
|
|
1602
1594
|
|
|
1603
|
-
if self._tbl_version_path.
|
|
1595
|
+
if not self._tbl_version_path.is_mutable():
|
|
1604
1596
|
return UpdateStatus()
|
|
1605
1597
|
# we lock the entire tree starting at the root base table in order to ensure that all synced columns can
|
|
1606
1598
|
# have their updates propagated down the tree
|
|
@@ -1711,3 +1703,9 @@ class Table(SchemaObject):
|
|
|
1711
1703
|
report_lines.append(report_line)
|
|
1712
1704
|
|
|
1713
1705
|
return pxt.dataframe.DataFrameResultSet(report_lines, self._REPORT_SCHEMA)
|
|
1706
|
+
|
|
1707
|
+
def __check_mutable(self, op_descr: str) -> None:
|
|
1708
|
+
if self._tbl_version_path.is_snapshot():
|
|
1709
|
+
raise excs.Error(f'{self._display_str()}: Cannot {op_descr} a snapshot.')
|
|
1710
|
+
if self._tbl_version_path.is_replica():
|
|
1711
|
+
raise excs.Error(f'{self._display_str()}: Cannot {op_descr} a {self._display_name()}.')
|