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
|
@@ -23,10 +23,11 @@ from pixeltable.utils.exception_handler import run_cleanup_on_exception
|
|
|
23
23
|
from pixeltable.utils.filecache import FileCache
|
|
24
24
|
from pixeltable.utils.media_store import MediaStore
|
|
25
25
|
|
|
26
|
+
from .tbl_ops import TableOp
|
|
27
|
+
|
|
26
28
|
if TYPE_CHECKING:
|
|
27
29
|
from pixeltable.plan import SampleClause
|
|
28
30
|
|
|
29
|
-
|
|
30
31
|
from ..func.globals import resolve_symbol
|
|
31
32
|
from .column import Column
|
|
32
33
|
from .globals import _POS_COLUMN_NAME, _ROWID_COLUMN_NAME, MediaValidation, is_valid_identifier
|
|
@@ -40,6 +41,19 @@ if TYPE_CHECKING:
|
|
|
40
41
|
_logger = logging.getLogger('pixeltable')
|
|
41
42
|
|
|
42
43
|
|
|
44
|
+
@dataclasses.dataclass(frozen=True)
|
|
45
|
+
class TableVersionMd:
|
|
46
|
+
"""
|
|
47
|
+
Complete set of md records for a specific TableVersion instance.
|
|
48
|
+
|
|
49
|
+
TODO: subsume schema.FullTableMd
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
tbl_md: schema.TableMd
|
|
53
|
+
version_md: schema.TableVersionMd
|
|
54
|
+
schema_version_md: schema.TableSchemaVersionMd
|
|
55
|
+
|
|
56
|
+
|
|
43
57
|
class TableVersion:
|
|
44
58
|
"""
|
|
45
59
|
TableVersion represents a particular version of a table/view along with its physical representation:
|
|
@@ -65,6 +79,7 @@ class TableVersion:
|
|
|
65
79
|
|
|
66
80
|
# record metadata stored in catalog
|
|
67
81
|
_tbl_md: schema.TableMd
|
|
82
|
+
_version_md: schema.TableVersionMd
|
|
68
83
|
_schema_version_md: schema.TableSchemaVersionMd
|
|
69
84
|
|
|
70
85
|
effective_version: Optional[int]
|
|
@@ -78,7 +93,7 @@ class TableVersion:
|
|
|
78
93
|
num_iterator_cols: int
|
|
79
94
|
|
|
80
95
|
# target for data operation propagation (only set for non-snapshots, and only records non-snapshot views)
|
|
81
|
-
mutable_views:
|
|
96
|
+
mutable_views: frozenset[TableVersionHandle]
|
|
82
97
|
|
|
83
98
|
# contains complete history of columns, incl dropped ones
|
|
84
99
|
cols: list[Column]
|
|
@@ -92,6 +107,8 @@ class TableVersion:
|
|
|
92
107
|
external_stores: dict[str, pxt.io.ExternalStore]
|
|
93
108
|
store_tbl: Optional['store.StoreBase']
|
|
94
109
|
|
|
110
|
+
is_initialized: bool # True if init() has been called
|
|
111
|
+
|
|
95
112
|
# used by Catalog to invalidate cached instances at the end of a transaction;
|
|
96
113
|
# True if this instance reflects the state of stored metadata in the context of this transaction and
|
|
97
114
|
# it is the instance cached in Catalog
|
|
@@ -110,6 +127,7 @@ class TableVersion:
|
|
|
110
127
|
self,
|
|
111
128
|
id: UUID,
|
|
112
129
|
tbl_md: schema.TableMd,
|
|
130
|
+
version_md: schema.TableVersionMd,
|
|
113
131
|
effective_version: Optional[int],
|
|
114
132
|
schema_version_md: schema.TableSchemaVersionMd,
|
|
115
133
|
mutable_views: list[TableVersionHandle],
|
|
@@ -117,8 +135,10 @@ class TableVersion:
|
|
|
117
135
|
base: Optional[TableVersionHandle] = None,
|
|
118
136
|
):
|
|
119
137
|
self.is_validated = True # a freshly constructed instance is always valid
|
|
138
|
+
self.is_initialized = False
|
|
120
139
|
self.id = id
|
|
121
140
|
self._tbl_md = copy.deepcopy(tbl_md)
|
|
141
|
+
self._version_md = copy.deepcopy(version_md)
|
|
122
142
|
self._schema_version_md = copy.deepcopy(schema_version_md)
|
|
123
143
|
self.effective_version = effective_version
|
|
124
144
|
assert not (self.is_view and base is None)
|
|
@@ -159,7 +179,7 @@ class TableVersion:
|
|
|
159
179
|
self.num_iterator_cols = len(output_schema)
|
|
160
180
|
assert tbl_md.view_md.iterator_args is not None
|
|
161
181
|
|
|
162
|
-
self.mutable_views =
|
|
182
|
+
self.mutable_views = frozenset(mutable_views)
|
|
163
183
|
assert self.is_mutable or len(self.mutable_views) == 0
|
|
164
184
|
|
|
165
185
|
self.cols = []
|
|
@@ -175,7 +195,9 @@ class TableVersion:
|
|
|
175
195
|
"""Create a snapshot copy of this TableVersion"""
|
|
176
196
|
assert not self.is_snapshot
|
|
177
197
|
base = self.path.base.tbl_version if self.is_view else None
|
|
178
|
-
return TableVersion(
|
|
198
|
+
return TableVersion(
|
|
199
|
+
self.id, self.tbl_md, self.version_md, self.version, self.schema_version_md, mutable_views=[], base=base
|
|
200
|
+
)
|
|
179
201
|
|
|
180
202
|
@property
|
|
181
203
|
def versioned_name(self) -> str:
|
|
@@ -190,6 +212,74 @@ class TableVersion:
|
|
|
190
212
|
|
|
191
213
|
return TableVersionHandle(self.id, self.effective_version, self)
|
|
192
214
|
|
|
215
|
+
@classmethod
|
|
216
|
+
def create_initial_md(
|
|
217
|
+
cls,
|
|
218
|
+
name: str,
|
|
219
|
+
cols: list[Column],
|
|
220
|
+
num_retained_versions: int,
|
|
221
|
+
comment: str,
|
|
222
|
+
media_validation: MediaValidation,
|
|
223
|
+
view_md: Optional[schema.ViewMd] = None,
|
|
224
|
+
) -> TableVersionMd:
|
|
225
|
+
user = Env.get().user
|
|
226
|
+
|
|
227
|
+
# assign ids
|
|
228
|
+
cols_by_name: dict[str, Column] = {}
|
|
229
|
+
for pos, col in enumerate(cols):
|
|
230
|
+
col.id = pos
|
|
231
|
+
col.schema_version_add = 0
|
|
232
|
+
cols_by_name[col.name] = col
|
|
233
|
+
if col.is_computed:
|
|
234
|
+
col.check_value_expr()
|
|
235
|
+
|
|
236
|
+
timestamp = time.time()
|
|
237
|
+
column_md = cls._create_column_md(cols)
|
|
238
|
+
tbl_id = uuid.uuid4()
|
|
239
|
+
tbl_id_str = str(tbl_id)
|
|
240
|
+
tbl_md = schema.TableMd(
|
|
241
|
+
tbl_id=tbl_id_str,
|
|
242
|
+
name=name,
|
|
243
|
+
user=user,
|
|
244
|
+
is_replica=False,
|
|
245
|
+
current_version=0,
|
|
246
|
+
current_schema_version=0,
|
|
247
|
+
next_col_id=len(cols),
|
|
248
|
+
next_idx_id=0,
|
|
249
|
+
next_row_id=0,
|
|
250
|
+
view_sn=0,
|
|
251
|
+
column_md=column_md,
|
|
252
|
+
index_md={},
|
|
253
|
+
external_stores=[],
|
|
254
|
+
view_md=view_md,
|
|
255
|
+
additional_md={},
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
table_version_md = schema.TableVersionMd(
|
|
259
|
+
tbl_id=tbl_id_str, created_at=timestamp, version=0, schema_version=0, additional_md={}
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
schema_col_md: dict[int, schema.SchemaColumn] = {}
|
|
263
|
+
for pos, col in enumerate(cols):
|
|
264
|
+
md = schema.SchemaColumn(
|
|
265
|
+
pos=pos,
|
|
266
|
+
name=col.name,
|
|
267
|
+
media_validation=col._media_validation.name.lower() if col._media_validation is not None else None,
|
|
268
|
+
)
|
|
269
|
+
schema_col_md[col.id] = md
|
|
270
|
+
|
|
271
|
+
schema_version_md = schema.TableSchemaVersionMd(
|
|
272
|
+
tbl_id=tbl_id_str,
|
|
273
|
+
schema_version=0,
|
|
274
|
+
preceding_schema_version=None,
|
|
275
|
+
columns=schema_col_md,
|
|
276
|
+
num_retained_versions=num_retained_versions,
|
|
277
|
+
comment=comment,
|
|
278
|
+
media_validation=media_validation.name.lower(),
|
|
279
|
+
additional_md={},
|
|
280
|
+
)
|
|
281
|
+
return TableVersionMd(tbl_md, table_version_md, schema_version_md)
|
|
282
|
+
|
|
193
283
|
@classmethod
|
|
194
284
|
def create(
|
|
195
285
|
cls,
|
|
@@ -199,8 +289,6 @@ class TableVersion:
|
|
|
199
289
|
num_retained_versions: int,
|
|
200
290
|
comment: str,
|
|
201
291
|
media_validation: MediaValidation,
|
|
202
|
-
# base_path: Optional[pxt.catalog.TableVersionPath] = None,
|
|
203
|
-
view_md: Optional[schema.ViewMd] = None,
|
|
204
292
|
) -> tuple[UUID, Optional[TableVersion]]:
|
|
205
293
|
user = Env.get().user
|
|
206
294
|
|
|
@@ -233,7 +321,7 @@ class TableVersion:
|
|
|
233
321
|
column_md=column_md,
|
|
234
322
|
index_md={},
|
|
235
323
|
external_stores=[],
|
|
236
|
-
view_md=
|
|
324
|
+
view_md=None,
|
|
237
325
|
additional_md={},
|
|
238
326
|
)
|
|
239
327
|
|
|
@@ -271,47 +359,15 @@ class TableVersion:
|
|
|
271
359
|
|
|
272
360
|
cat = pxt.catalog.Catalog.get()
|
|
273
361
|
|
|
274
|
-
|
|
275
|
-
# predicate to apply at runtime), we don't create a physical table and simply use the base's table version path
|
|
276
|
-
if (
|
|
277
|
-
view_md is not None
|
|
278
|
-
and view_md.is_snapshot
|
|
279
|
-
and view_md.predicate is None
|
|
280
|
-
and view_md.sample_clause is None
|
|
281
|
-
and len(cols) == 0
|
|
282
|
-
):
|
|
283
|
-
cat.store_tbl_md(
|
|
284
|
-
tbl_id=tbl_id,
|
|
285
|
-
dir_id=dir_id,
|
|
286
|
-
tbl_md=table_md,
|
|
287
|
-
version_md=table_version_md,
|
|
288
|
-
schema_version_md=schema_version_md,
|
|
289
|
-
)
|
|
290
|
-
return tbl_id, None
|
|
291
|
-
|
|
292
|
-
# assert (base_path is not None) == (view_md is not None)
|
|
293
|
-
is_snapshot = view_md is not None and view_md.is_snapshot
|
|
294
|
-
effective_version = 0 if is_snapshot else None
|
|
295
|
-
base_path = pxt.catalog.TableVersionPath.from_md(view_md.base_versions) if view_md is not None else None
|
|
296
|
-
base = base_path.tbl_version if base_path is not None else None
|
|
297
|
-
tbl_version = cls(tbl_id, table_md, effective_version, schema_version_md, [], base_path=base_path, base=base)
|
|
362
|
+
tbl_version = cls(tbl_id, table_md, table_version_md, None, schema_version_md, [])
|
|
298
363
|
# TODO: break this up, so that Catalog.create_table() registers tbl_version
|
|
299
|
-
cat._tbl_versions[tbl_id,
|
|
364
|
+
cat._tbl_versions[tbl_id, None] = tbl_version
|
|
300
365
|
tbl_version.init()
|
|
301
366
|
tbl_version.store_tbl.create()
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
handle = TableVersionHandle(tbl_version.id, effective_version)
|
|
307
|
-
assert handle not in base.get().mutable_views
|
|
308
|
-
base.get().mutable_views.add(handle)
|
|
309
|
-
|
|
310
|
-
if view_md is None or not view_md.is_snapshot:
|
|
311
|
-
# add default indices, after creating the store table
|
|
312
|
-
for col in tbl_version.cols_by_name.values():
|
|
313
|
-
status = tbl_version._add_default_index(col)
|
|
314
|
-
assert status is None or status.num_excs == 0
|
|
367
|
+
# add default indices, after creating the store table
|
|
368
|
+
for col in tbl_version.cols_by_name.values():
|
|
369
|
+
status = tbl_version._add_default_index(col)
|
|
370
|
+
assert status is None or status.num_excs == 0
|
|
315
371
|
|
|
316
372
|
cat.store_tbl_md(
|
|
317
373
|
tbl_id=tbl_id,
|
|
@@ -322,6 +378,27 @@ class TableVersion:
|
|
|
322
378
|
)
|
|
323
379
|
return tbl_id, tbl_version
|
|
324
380
|
|
|
381
|
+
def exec_op(self, op: TableOp) -> None:
|
|
382
|
+
if op.create_store_table_op is not None:
|
|
383
|
+
# don't use Catalog.begin_xact() here, to avoid accidental recursive calls to exec_op()
|
|
384
|
+
with Env.get().begin_xact():
|
|
385
|
+
self.store_tbl.create()
|
|
386
|
+
|
|
387
|
+
elif op.load_view_op is not None:
|
|
388
|
+
from pixeltable.catalog import Catalog
|
|
389
|
+
from pixeltable.plan import Planner
|
|
390
|
+
|
|
391
|
+
from .table_version_path import TableVersionPath
|
|
392
|
+
|
|
393
|
+
# clear out any remaining media files from an aborted previous attempt
|
|
394
|
+
MediaStore.delete(self.id)
|
|
395
|
+
view_path = TableVersionPath.from_dict(op.load_view_op.view_path)
|
|
396
|
+
plan, _ = Planner.create_view_load_plan(view_path)
|
|
397
|
+
_, row_counts = self.store_tbl.insert_rows(plan, v_min=self.version)
|
|
398
|
+
status = UpdateStatus(row_count_stats=row_counts)
|
|
399
|
+
Catalog.get().store_update_status(self.id, self.version, status)
|
|
400
|
+
_logger.debug(f'Loaded view {self.name} with {row_counts.num_rows} rows')
|
|
401
|
+
|
|
325
402
|
@classmethod
|
|
326
403
|
def create_replica(cls, md: schema.FullTableMd) -> TableVersion:
|
|
327
404
|
assert Env.get().in_xact
|
|
@@ -331,7 +408,14 @@ class TableVersion:
|
|
|
331
408
|
base_path = pxt.catalog.TableVersionPath.from_md(view_md.base_versions) if view_md is not None else None
|
|
332
409
|
base = base_path.tbl_version if base_path is not None else None
|
|
333
410
|
tbl_version = cls(
|
|
334
|
-
tbl_id,
|
|
411
|
+
tbl_id,
|
|
412
|
+
md.tbl_md,
|
|
413
|
+
md.version_md,
|
|
414
|
+
md.version_md.version,
|
|
415
|
+
md.schema_version_md,
|
|
416
|
+
[],
|
|
417
|
+
base_path=base_path,
|
|
418
|
+
base=base,
|
|
335
419
|
)
|
|
336
420
|
cat = pxt.catalog.Catalog.get()
|
|
337
421
|
# We're creating a new TableVersion replica, so we should never have seen this particular
|
|
@@ -345,23 +429,18 @@ class TableVersion:
|
|
|
345
429
|
return tbl_version
|
|
346
430
|
|
|
347
431
|
def drop(self) -> None:
|
|
348
|
-
if self.is_view and self.is_mutable:
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
432
|
+
# if self.is_view and self.is_mutable:
|
|
433
|
+
# # update mutable_views
|
|
434
|
+
# # TODO: invalidate base to force reload
|
|
435
|
+
# from .table_version_handle import TableVersionHandle
|
|
436
|
+
#
|
|
437
|
+
# assert self.base is not None
|
|
438
|
+
# if self.base.get().is_mutable:
|
|
439
|
+
# self.base.get().mutable_views.remove(TableVersionHandle.create(self))
|
|
356
440
|
|
|
357
|
-
# cat = Catalog.get()
|
|
358
|
-
# delete this table and all associated data
|
|
359
441
|
MediaStore.delete(self.id)
|
|
360
442
|
FileCache.get().clear(tbl_id=self.id)
|
|
361
|
-
# cat.delete_tbl_md(self.id)
|
|
362
443
|
self.store_tbl.drop()
|
|
363
|
-
# de-register table version from catalog
|
|
364
|
-
# cat.remove_tbl_version(self)
|
|
365
444
|
|
|
366
445
|
def init(self) -> None:
|
|
367
446
|
"""
|
|
@@ -373,11 +452,11 @@ class TableVersion:
|
|
|
373
452
|
cat = Catalog.get()
|
|
374
453
|
assert (self.id, self.effective_version) in cat._tbl_versions
|
|
375
454
|
self._init_schema()
|
|
376
|
-
if
|
|
455
|
+
if self.is_mutable:
|
|
377
456
|
cat.record_column_dependencies(self)
|
|
378
|
-
|
|
379
457
|
# init external stores; this needs to happen after the schema is created
|
|
380
458
|
self._init_external_stores()
|
|
459
|
+
self.is_initialized = True
|
|
381
460
|
|
|
382
461
|
def _init_schema(self) -> None:
|
|
383
462
|
# create columns first, so the indices can reference them
|
|
@@ -453,7 +532,14 @@ class TableVersion:
|
|
|
453
532
|
# instantiate index object
|
|
454
533
|
cls_name = md.class_fqn.rsplit('.', 1)[-1]
|
|
455
534
|
cls = getattr(index_module, cls_name)
|
|
456
|
-
idx_col
|
|
535
|
+
idx_col: Column
|
|
536
|
+
if md.indexed_col_tbl_id == str(self.id):
|
|
537
|
+
# this is a reference to one of our columns: avoid TVP.get_column_by_id() here, because we're not fully
|
|
538
|
+
# initialized yet
|
|
539
|
+
idx_col = self.cols_by_id[md.indexed_col_id]
|
|
540
|
+
else:
|
|
541
|
+
assert self.path.base is not None
|
|
542
|
+
idx_col = self.path.base.get_column_by_id(UUID(md.indexed_col_tbl_id), md.indexed_col_id)
|
|
457
543
|
idx = cls.from_dict(idx_col, md.init_args)
|
|
458
544
|
|
|
459
545
|
# fix up the sa column type of the index value and undo columns
|
|
@@ -478,40 +564,17 @@ class TableVersion:
|
|
|
478
564
|
else:
|
|
479
565
|
self.store_tbl = StoreTable(self)
|
|
480
566
|
|
|
481
|
-
def _write_md(
|
|
482
|
-
self,
|
|
483
|
-
new_version: bool,
|
|
484
|
-
new_version_ts: float,
|
|
485
|
-
new_schema_version: bool,
|
|
486
|
-
update_status: Optional[UpdateStatus] = None,
|
|
487
|
-
) -> None:
|
|
488
|
-
"""Writes table metadata to the database.
|
|
489
|
-
|
|
490
|
-
Args:
|
|
491
|
-
timestamp: timestamp of the change
|
|
492
|
-
update_tbl_version: if `True`, will also write `TableVersion` metadata
|
|
493
|
-
preceding_schema_version: if specified, will also write `TableSchemaVersion` metadata, recording the
|
|
494
|
-
specified preceding schema version
|
|
495
|
-
"""
|
|
567
|
+
def _write_md(self, new_version: bool, new_schema_version: bool) -> None:
|
|
496
568
|
from pixeltable.catalog import Catalog
|
|
497
569
|
|
|
498
|
-
version_md = self._create_version_md(new_version_ts, update_status=update_status) if new_version else None
|
|
499
|
-
|
|
500
570
|
Catalog.get().store_tbl_md(
|
|
501
|
-
self.id,
|
|
571
|
+
self.id,
|
|
572
|
+
None,
|
|
573
|
+
self._tbl_md,
|
|
574
|
+
self._version_md if new_version else None,
|
|
575
|
+
self._schema_version_md if new_schema_version else None,
|
|
502
576
|
)
|
|
503
577
|
|
|
504
|
-
def _write_md_update_status(self, new_version_ts: float, update_status: UpdateStatus) -> None:
|
|
505
|
-
"""Writes a new update_status in the table version metadata in the database.
|
|
506
|
-
|
|
507
|
-
Args:
|
|
508
|
-
timestamp: timestamp of the change
|
|
509
|
-
update_status: UpdateStatus to be updated in the database
|
|
510
|
-
"""
|
|
511
|
-
from pixeltable.catalog import Catalog
|
|
512
|
-
|
|
513
|
-
Catalog.get().update_tbl_version_md(self._create_version_md(new_version_ts, update_status))
|
|
514
|
-
|
|
515
578
|
def _store_idx_name(self, idx_id: int) -> str:
|
|
516
579
|
"""Return name of index in the store, which needs to be globally unique"""
|
|
517
580
|
return f'idx_{self.id.hex}_{idx_id}'
|
|
@@ -519,10 +582,10 @@ class TableVersion:
|
|
|
519
582
|
def add_index(self, col: Column, idx_name: Optional[str], idx: index.IndexBase) -> UpdateStatus:
|
|
520
583
|
# we're creating a new schema version
|
|
521
584
|
self.version += 1
|
|
522
|
-
self.
|
|
585
|
+
self.created_at = time.time()
|
|
523
586
|
self.schema_version = self.version
|
|
524
587
|
status = self._add_index(col, idx_name, idx)
|
|
525
|
-
self._write_md(new_version=True,
|
|
588
|
+
self._write_md(new_version=True, new_schema_version=True)
|
|
526
589
|
_logger.info(f'Added index {idx_name} on column {col.name} to table {self.name}')
|
|
527
590
|
return status
|
|
528
591
|
|
|
@@ -638,12 +701,12 @@ class TableVersion:
|
|
|
638
701
|
return status
|
|
639
702
|
|
|
640
703
|
def drop_index(self, idx_id: int) -> None:
|
|
641
|
-
assert
|
|
704
|
+
assert self.is_mutable
|
|
642
705
|
assert idx_id in self._tbl_md.index_md
|
|
643
706
|
|
|
644
707
|
# we're creating a new schema version
|
|
645
708
|
self.version += 1
|
|
646
|
-
self.
|
|
709
|
+
self.created_at = time.time()
|
|
647
710
|
self.schema_version = self.version
|
|
648
711
|
idx_md = self._tbl_md.index_md[idx_id]
|
|
649
712
|
idx_md.schema_version_drop = self.schema_version
|
|
@@ -656,14 +719,14 @@ class TableVersion:
|
|
|
656
719
|
del self._tbl_md.index_md[idx_id]
|
|
657
720
|
|
|
658
721
|
self._drop_columns([idx_info.val_col, idx_info.undo_col])
|
|
659
|
-
self._write_md(new_version=True,
|
|
722
|
+
self._write_md(new_version=True, new_schema_version=True)
|
|
660
723
|
_logger.info(f'Dropped index {idx_md.name} on table {self.name}')
|
|
661
724
|
|
|
662
725
|
def add_columns(
|
|
663
726
|
self, cols: Iterable[Column], print_stats: bool, on_error: Literal['abort', 'ignore']
|
|
664
727
|
) -> UpdateStatus:
|
|
665
728
|
"""Adds columns to the table."""
|
|
666
|
-
assert
|
|
729
|
+
assert self.is_mutable
|
|
667
730
|
assert all(is_valid_identifier(col.name) for col in cols if col.name is not None)
|
|
668
731
|
assert all(col.stored is not None for col in cols)
|
|
669
732
|
assert all(col.name not in self.cols_by_name for col in cols if col.name is not None)
|
|
@@ -674,7 +737,7 @@ class TableVersion:
|
|
|
674
737
|
|
|
675
738
|
# we're creating a new schema version
|
|
676
739
|
self.version += 1
|
|
677
|
-
self.
|
|
740
|
+
self.created_at = time.time()
|
|
678
741
|
self.schema_version = self.version
|
|
679
742
|
index_cols: dict[Column, tuple[index.BtreeIndex, Column, Column]] = {}
|
|
680
743
|
all_cols: list[Column] = []
|
|
@@ -691,7 +754,8 @@ class TableVersion:
|
|
|
691
754
|
# Create indices and their md records
|
|
692
755
|
for col, (idx, val_col, undo_col) in index_cols.items():
|
|
693
756
|
self._create_index(col, val_col, undo_col, idx_name=None, idx=idx)
|
|
694
|
-
self.
|
|
757
|
+
self.update_status = status
|
|
758
|
+
self._write_md(new_version=True, new_schema_version=True)
|
|
695
759
|
_logger.info(f'Added columns {[col.name for col in cols]} to table {self.name}, new version: {self.version}')
|
|
696
760
|
|
|
697
761
|
msg = (
|
|
@@ -709,6 +773,7 @@ class TableVersion:
|
|
|
709
773
|
cols_to_add = list(cols)
|
|
710
774
|
row_count = self.store_tbl.count()
|
|
711
775
|
for col in cols_to_add:
|
|
776
|
+
assert col.tbl is self
|
|
712
777
|
if not col.col_type.nullable and not col.is_computed and row_count > 0:
|
|
713
778
|
raise excs.Error(
|
|
714
779
|
f'Cannot add non-nullable column {col.name!r} to table {self.name!r} with existing rows'
|
|
@@ -801,11 +866,11 @@ class TableVersion:
|
|
|
801
866
|
def drop_column(self, col: Column) -> None:
|
|
802
867
|
"""Drop a column from the table."""
|
|
803
868
|
|
|
804
|
-
assert
|
|
869
|
+
assert self.is_mutable
|
|
805
870
|
|
|
806
871
|
# we're creating a new schema version
|
|
807
872
|
self.version += 1
|
|
808
|
-
self.
|
|
873
|
+
self.created_at = time.time()
|
|
809
874
|
self.schema_version = self.version
|
|
810
875
|
|
|
811
876
|
# drop this column and all dependent index columns and indices
|
|
@@ -825,12 +890,12 @@ class TableVersion:
|
|
|
825
890
|
del self.idxs_by_name[idx_name]
|
|
826
891
|
|
|
827
892
|
self._drop_columns(dropped_cols)
|
|
828
|
-
self._write_md(new_version=True,
|
|
893
|
+
self._write_md(new_version=True, new_schema_version=True)
|
|
829
894
|
_logger.info(f'Dropped column {col.name} from table {self.name}, new version: {self.version}')
|
|
830
895
|
|
|
831
896
|
def _drop_columns(self, cols: Iterable[Column]) -> None:
|
|
832
897
|
"""Mark columns as dropped"""
|
|
833
|
-
assert
|
|
898
|
+
assert self.is_mutable
|
|
834
899
|
|
|
835
900
|
for col in cols:
|
|
836
901
|
col.schema_version_drop = self.schema_version
|
|
@@ -853,7 +918,7 @@ class TableVersion:
|
|
|
853
918
|
|
|
854
919
|
def rename_column(self, old_name: str, new_name: str) -> None:
|
|
855
920
|
"""Rename a column."""
|
|
856
|
-
assert
|
|
921
|
+
assert self.is_mutable
|
|
857
922
|
if old_name not in self.cols_by_name:
|
|
858
923
|
raise excs.Error(f'Unknown column: {old_name}')
|
|
859
924
|
if not is_valid_identifier(new_name):
|
|
@@ -868,10 +933,10 @@ class TableVersion:
|
|
|
868
933
|
|
|
869
934
|
# we're creating a new schema version
|
|
870
935
|
self.version += 1
|
|
871
|
-
self.
|
|
936
|
+
self.created_at = time.time()
|
|
872
937
|
self.schema_version = self.version
|
|
873
938
|
|
|
874
|
-
self._write_md(new_version=True,
|
|
939
|
+
self._write_md(new_version=True, new_schema_version=True)
|
|
875
940
|
_logger.info(f'Renamed column {old_name} to {new_name} in table {self.name}, new version: {self.version}')
|
|
876
941
|
|
|
877
942
|
def set_comment(self, new_comment: Optional[str]) -> None:
|
|
@@ -890,9 +955,9 @@ class TableVersion:
|
|
|
890
955
|
def _create_schema_version(self) -> None:
|
|
891
956
|
# we're creating a new schema version
|
|
892
957
|
self.version += 1
|
|
893
|
-
self.
|
|
958
|
+
self.created_at = time.time()
|
|
894
959
|
self.schema_version = self.version
|
|
895
|
-
self._write_md(new_version=True,
|
|
960
|
+
self._write_md(new_version=True, new_schema_version=True)
|
|
896
961
|
_logger.info(f'[{self.name}] Updating table schema to version: {self.version}')
|
|
897
962
|
|
|
898
963
|
def insert(
|
|
@@ -939,6 +1004,7 @@ class TableVersion:
|
|
|
939
1004
|
"""Insert rows produced by exec_plan and propagate to views"""
|
|
940
1005
|
# we're creating a new version
|
|
941
1006
|
self.version += 1
|
|
1007
|
+
self.created_at = timestamp
|
|
942
1008
|
cols_with_excs, row_counts = self.store_tbl.insert_rows(
|
|
943
1009
|
exec_plan, v_min=self.version, rowids=rowids, abort_on_exc=abort_on_exc
|
|
944
1010
|
)
|
|
@@ -956,7 +1022,8 @@ class TableVersion:
|
|
|
956
1022
|
result += status.to_cascade()
|
|
957
1023
|
|
|
958
1024
|
# Use the net status after all propagations
|
|
959
|
-
self.
|
|
1025
|
+
self.update_status = result
|
|
1026
|
+
self._write_md(new_version=True, new_schema_version=False)
|
|
960
1027
|
if print_stats:
|
|
961
1028
|
exec_plan.ctx.profile.print(num_rows=result.num_rows)
|
|
962
1029
|
_logger.info(f'TableVersion {self.name}: new version {self.version}')
|
|
@@ -972,8 +1039,7 @@ class TableVersion:
|
|
|
972
1039
|
cascade: if True, also update all computed columns that transitively depend on the updated columns,
|
|
973
1040
|
including within views.
|
|
974
1041
|
"""
|
|
975
|
-
|
|
976
|
-
raise excs.Error('Cannot update a snapshot')
|
|
1042
|
+
assert self.is_mutable
|
|
977
1043
|
|
|
978
1044
|
from pixeltable.plan import Planner
|
|
979
1045
|
|
|
@@ -1087,7 +1153,7 @@ class TableVersion:
|
|
|
1087
1153
|
return update_targets
|
|
1088
1154
|
|
|
1089
1155
|
def recompute_columns(self, col_names: list[str], errors_only: bool = False, cascade: bool = True) -> UpdateStatus:
|
|
1090
|
-
assert
|
|
1156
|
+
assert self.is_mutable
|
|
1091
1157
|
assert all(name in self.cols_by_name for name in col_names)
|
|
1092
1158
|
assert len(col_names) > 0
|
|
1093
1159
|
assert len(col_names) == 1 or not errors_only
|
|
@@ -1132,6 +1198,7 @@ class TableVersion:
|
|
|
1132
1198
|
create_new_table_version = plan is not None
|
|
1133
1199
|
if create_new_table_version:
|
|
1134
1200
|
self.version += 1
|
|
1201
|
+
self.created_at = timestamp
|
|
1135
1202
|
cols_with_excs, row_counts = self.store_tbl.insert_rows(
|
|
1136
1203
|
plan, v_min=self.version, show_progress=show_progress
|
|
1137
1204
|
)
|
|
@@ -1158,7 +1225,8 @@ class TableVersion:
|
|
|
1158
1225
|
)
|
|
1159
1226
|
result += status.to_cascade()
|
|
1160
1227
|
if create_new_table_version:
|
|
1161
|
-
self.
|
|
1228
|
+
self.update_status = result
|
|
1229
|
+
self._write_md(new_version=True, new_schema_version=False)
|
|
1162
1230
|
return result
|
|
1163
1231
|
|
|
1164
1232
|
def delete(self, where: Optional[exprs.Expr] = None) -> UpdateStatus:
|
|
@@ -1212,18 +1280,21 @@ class TableVersion:
|
|
|
1212
1280
|
if del_rows > 0:
|
|
1213
1281
|
# we're creating a new version
|
|
1214
1282
|
self.version += 1
|
|
1283
|
+
self.created_at = timestamp
|
|
1215
1284
|
for view in self.mutable_views:
|
|
1216
1285
|
status = view.get().propagate_delete(
|
|
1217
1286
|
where=None, base_versions=[self.version, *base_versions], timestamp=timestamp
|
|
1218
1287
|
)
|
|
1219
1288
|
result += status.to_cascade()
|
|
1289
|
+
self.update_status = result
|
|
1290
|
+
|
|
1220
1291
|
if del_rows > 0:
|
|
1221
|
-
self._write_md(new_version=True,
|
|
1292
|
+
self._write_md(new_version=True, new_schema_version=False)
|
|
1222
1293
|
return result
|
|
1223
1294
|
|
|
1224
1295
|
def revert(self) -> None:
|
|
1225
1296
|
"""Reverts the table to the previous version."""
|
|
1226
|
-
assert
|
|
1297
|
+
assert self.is_mutable
|
|
1227
1298
|
if self.version == 0:
|
|
1228
1299
|
raise excs.Error('Cannot revert version 0')
|
|
1229
1300
|
self._revert()
|
|
@@ -1255,7 +1326,7 @@ class TableVersion:
|
|
|
1255
1326
|
)
|
|
1256
1327
|
|
|
1257
1328
|
# delete newly-added data
|
|
1258
|
-
MediaStore.delete(self.id,
|
|
1329
|
+
MediaStore.delete(self.id, tbl_version=self.version)
|
|
1259
1330
|
conn.execute(sql.delete(self.store_tbl.sa_tbl).where(self.store_tbl.sa_tbl.c.v_min == self.version))
|
|
1260
1331
|
|
|
1261
1332
|
# revert new deletions
|
|
@@ -1317,7 +1388,7 @@ class TableVersion:
|
|
|
1317
1388
|
)
|
|
1318
1389
|
|
|
1319
1390
|
self.version -= 1
|
|
1320
|
-
self._write_md(new_version=False,
|
|
1391
|
+
self._write_md(new_version=False, new_schema_version=False)
|
|
1321
1392
|
|
|
1322
1393
|
# propagate to views
|
|
1323
1394
|
views_str = ', '.join([str(v.id) for v in self.mutable_views])
|
|
@@ -1339,28 +1410,32 @@ class TableVersion:
|
|
|
1339
1410
|
|
|
1340
1411
|
def link_external_store(self, store: pxt.io.ExternalStore) -> None:
|
|
1341
1412
|
self.version += 1
|
|
1342
|
-
self.
|
|
1413
|
+
self.created_at = time.time()
|
|
1343
1414
|
self.schema_version = self.version
|
|
1344
1415
|
|
|
1345
1416
|
self.external_stores[store.name] = store
|
|
1346
1417
|
self._tbl_md.external_stores.append(
|
|
1347
1418
|
{'class': f'{type(store).__module__}.{type(store).__qualname__}', 'md': store.as_dict()}
|
|
1348
1419
|
)
|
|
1349
|
-
self._write_md(new_version=True,
|
|
1420
|
+
self._write_md(new_version=True, new_schema_version=True)
|
|
1350
1421
|
|
|
1351
1422
|
def unlink_external_store(self, store: pxt.io.ExternalStore) -> None:
|
|
1352
1423
|
del self.external_stores[store.name]
|
|
1353
1424
|
self.version += 1
|
|
1354
|
-
self.
|
|
1425
|
+
self.created_at = time.time()
|
|
1355
1426
|
self.schema_version = self.version
|
|
1356
1427
|
idx = next(i for i, store_md in enumerate(self._tbl_md.external_stores) if store_md['md']['name'] == store.name)
|
|
1357
1428
|
self._tbl_md.external_stores.pop(idx)
|
|
1358
|
-
self._write_md(new_version=True,
|
|
1429
|
+
self._write_md(new_version=True, new_schema_version=True)
|
|
1359
1430
|
|
|
1360
1431
|
@property
|
|
1361
1432
|
def tbl_md(self) -> schema.TableMd:
|
|
1362
1433
|
return self._tbl_md
|
|
1363
1434
|
|
|
1435
|
+
@property
|
|
1436
|
+
def version_md(self) -> schema.TableVersionMd:
|
|
1437
|
+
return self._version_md
|
|
1438
|
+
|
|
1364
1439
|
@property
|
|
1365
1440
|
def schema_version_md(self) -> schema.TableSchemaVersionMd:
|
|
1366
1441
|
return self._schema_version_md
|
|
@@ -1408,6 +1483,16 @@ class TableVersion:
|
|
|
1408
1483
|
def version(self, version: int) -> None:
|
|
1409
1484
|
assert self.effective_version is None
|
|
1410
1485
|
self._tbl_md.current_version = version
|
|
1486
|
+
self._version_md.version = version
|
|
1487
|
+
|
|
1488
|
+
@property
|
|
1489
|
+
def created_at(self) -> float:
|
|
1490
|
+
return self._version_md.created_at
|
|
1491
|
+
|
|
1492
|
+
@created_at.setter
|
|
1493
|
+
def created_at(self, ts: float) -> None:
|
|
1494
|
+
assert self.effective_version is None
|
|
1495
|
+
self._version_md.created_at = ts
|
|
1411
1496
|
|
|
1412
1497
|
@property
|
|
1413
1498
|
def schema_version(self) -> int:
|
|
@@ -1417,16 +1502,22 @@ class TableVersion:
|
|
|
1417
1502
|
def schema_version(self, version: int) -> None:
|
|
1418
1503
|
assert self.effective_version is None
|
|
1419
1504
|
self._tbl_md.current_schema_version = version
|
|
1505
|
+
self._version_md.schema_version = version
|
|
1506
|
+
self._schema_version_md.preceding_schema_version = self._schema_version_md.schema_version
|
|
1420
1507
|
self._schema_version_md.schema_version = version
|
|
1421
1508
|
|
|
1422
1509
|
@property
|
|
1423
1510
|
def preceding_schema_version(self) -> int:
|
|
1424
1511
|
return self._schema_version_md.preceding_schema_version
|
|
1425
1512
|
|
|
1426
|
-
@
|
|
1427
|
-
def
|
|
1513
|
+
@property
|
|
1514
|
+
def update_status(self) -> Optional[UpdateStatus]:
|
|
1515
|
+
return self._version_md.update_status
|
|
1516
|
+
|
|
1517
|
+
@update_status.setter
|
|
1518
|
+
def update_status(self, status: UpdateStatus) -> None:
|
|
1428
1519
|
assert self.effective_version is None
|
|
1429
|
-
self.
|
|
1520
|
+
self._version_md.update_status = status
|
|
1430
1521
|
|
|
1431
1522
|
@property
|
|
1432
1523
|
def media_validation(self) -> MediaValidation:
|
|
@@ -1482,7 +1573,7 @@ class TableVersion:
|
|
|
1482
1573
|
@property
|
|
1483
1574
|
def is_insertable(self) -> bool:
|
|
1484
1575
|
"""Returns True if this corresponds to an InsertableTable"""
|
|
1485
|
-
return
|
|
1576
|
+
return self.is_mutable and not self.is_view
|
|
1486
1577
|
|
|
1487
1578
|
def is_iterator_column(self, col: Column) -> bool:
|
|
1488
1579
|
"""Returns True if col is produced by an iterator"""
|
|
@@ -1560,37 +1651,6 @@ class TableVersion:
|
|
|
1560
1651
|
{'class': f'{type(store).__module__}.{type(store).__qualname__}', 'md': store.as_dict()} for store in stores
|
|
1561
1652
|
]
|
|
1562
1653
|
|
|
1563
|
-
def _create_version_md(self, timestamp: float, update_status: Optional[UpdateStatus]) -> schema.TableVersionMd:
|
|
1564
|
-
return schema.TableVersionMd(
|
|
1565
|
-
tbl_id=str(self.id),
|
|
1566
|
-
created_at=timestamp,
|
|
1567
|
-
version=self.version,
|
|
1568
|
-
schema_version=self.schema_version,
|
|
1569
|
-
user=Env.get().user,
|
|
1570
|
-
update_status=update_status,
|
|
1571
|
-
additional_md={},
|
|
1572
|
-
)
|
|
1573
|
-
|
|
1574
|
-
def _create_schema_version_md(self, preceding_schema_version: int) -> schema.TableSchemaVersionMd:
|
|
1575
|
-
column_md: dict[int, schema.SchemaColumn] = {}
|
|
1576
|
-
for pos, col in enumerate(self.cols_by_name.values()):
|
|
1577
|
-
column_md[col.id] = schema.SchemaColumn(
|
|
1578
|
-
pos=pos,
|
|
1579
|
-
name=col.name,
|
|
1580
|
-
media_validation=col._media_validation.name.lower() if col._media_validation is not None else None,
|
|
1581
|
-
)
|
|
1582
|
-
# preceding_schema_version to be set by the caller
|
|
1583
|
-
return schema.TableSchemaVersionMd(
|
|
1584
|
-
tbl_id=str(self.id),
|
|
1585
|
-
schema_version=self.schema_version,
|
|
1586
|
-
preceding_schema_version=preceding_schema_version,
|
|
1587
|
-
columns=column_md,
|
|
1588
|
-
num_retained_versions=self.num_retained_versions,
|
|
1589
|
-
comment=self.comment,
|
|
1590
|
-
media_validation=self.media_validation.name.lower(),
|
|
1591
|
-
additional_md={},
|
|
1592
|
-
)
|
|
1593
|
-
|
|
1594
1654
|
def as_dict(self) -> dict:
|
|
1595
1655
|
return {'id': str(self.id), 'effective_version': self.effective_version}
|
|
1596
1656
|
|