pixeltable 0.3.14__py3-none-any.whl → 0.4.0rc1__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/catalog.py +292 -105
- pixeltable/catalog/column.py +10 -8
- pixeltable/catalog/dir.py +1 -2
- pixeltable/catalog/insertable_table.py +25 -20
- pixeltable/catalog/schema_object.py +3 -6
- pixeltable/catalog/table.py +245 -189
- pixeltable/catalog/table_version.py +319 -201
- pixeltable/catalog/table_version_handle.py +15 -2
- pixeltable/catalog/table_version_path.py +60 -21
- pixeltable/catalog/view.py +14 -5
- pixeltable/dataframe.py +11 -9
- pixeltable/env.py +2 -4
- pixeltable/exec/in_memory_data_node.py +1 -1
- pixeltable/exec/sql_node.py +20 -11
- pixeltable/exprs/column_property_ref.py +15 -6
- pixeltable/exprs/column_ref.py +32 -11
- pixeltable/exprs/comparison.py +1 -1
- pixeltable/exprs/row_builder.py +4 -6
- pixeltable/exprs/rowid_ref.py +8 -0
- pixeltable/exprs/similarity_expr.py +1 -0
- pixeltable/func/query_template_function.py +1 -1
- pixeltable/functions/gemini.py +166 -33
- pixeltable/functions/math.py +63 -0
- pixeltable/functions/string.py +212 -58
- pixeltable/globals.py +7 -4
- pixeltable/index/base.py +5 -0
- pixeltable/index/btree.py +5 -0
- pixeltable/index/embedding_index.py +5 -0
- pixeltable/io/external_store.py +8 -29
- pixeltable/io/label_studio.py +1 -1
- pixeltable/io/parquet.py +4 -4
- pixeltable/io/table_data_conduit.py +0 -31
- pixeltable/metadata/__init__.py +1 -1
- pixeltable/metadata/converters/convert_13.py +2 -2
- pixeltable/metadata/converters/convert_30.py +6 -11
- pixeltable/metadata/converters/convert_35.py +9 -0
- pixeltable/metadata/converters/util.py +3 -9
- pixeltable/metadata/notes.py +1 -0
- pixeltable/metadata/schema.py +5 -1
- pixeltable/plan.py +4 -4
- pixeltable/share/packager.py +207 -15
- pixeltable/share/publish.py +2 -2
- pixeltable/store.py +31 -13
- pixeltable/utils/dbms.py +1 -1
- {pixeltable-0.3.14.dist-info → pixeltable-0.4.0rc1.dist-info}/METADATA +1 -1
- {pixeltable-0.3.14.dist-info → pixeltable-0.4.0rc1.dist-info}/RECORD +50 -49
- {pixeltable-0.3.14.dist-info → pixeltable-0.4.0rc1.dist-info}/LICENSE +0 -0
- {pixeltable-0.3.14.dist-info → pixeltable-0.4.0rc1.dist-info}/WHEEL +0 -0
- {pixeltable-0.3.14.dist-info → pixeltable-0.4.0rc1.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import copy
|
|
3
4
|
import dataclasses
|
|
4
5
|
import importlib
|
|
5
6
|
import logging
|
|
@@ -50,43 +51,44 @@ class TableVersion:
|
|
|
50
51
|
|
|
51
52
|
Instances of TableVersion should not be stored as member variables (ie, used across transaction boundaries).
|
|
52
53
|
Use a TableVersionHandle instead.
|
|
54
|
+
|
|
55
|
+
Only TableVersion and Catalog interact directly with stored metadata. Everything else needs to go through these
|
|
56
|
+
two classes.
|
|
53
57
|
"""
|
|
54
58
|
|
|
55
59
|
id: UUID
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
|
|
61
|
+
# record metadata stored in catalog
|
|
62
|
+
_tbl_md: schema.TableMd
|
|
63
|
+
_schema_version_md: schema.TableSchemaVersionMd
|
|
64
|
+
|
|
58
65
|
effective_version: Optional[int]
|
|
59
|
-
is_replica: bool
|
|
60
|
-
version: int
|
|
61
|
-
comment: str
|
|
62
|
-
media_validation: MediaValidation
|
|
63
|
-
num_retained_versions: int
|
|
64
|
-
schema_version: int
|
|
65
|
-
view_md: Optional[schema.ViewMd]
|
|
66
66
|
path: Optional[pxt.catalog.TableVersionPath] # only set for live tables; needed to resolve computed cols
|
|
67
67
|
base: Optional[TableVersionHandle] # only set for views
|
|
68
|
-
next_col_id: int
|
|
69
|
-
next_idx_id: int
|
|
70
|
-
next_rowid: int
|
|
71
68
|
predicate: Optional[exprs.Expr]
|
|
72
|
-
mutable_views: list[TableVersionHandle] # target for data operation propagation (only set for live tables)
|
|
73
69
|
iterator_cls: Optional[type[ComponentIterator]]
|
|
74
70
|
iterator_args: Optional[exprs.InlineDict]
|
|
75
71
|
num_iterator_cols: int
|
|
76
72
|
|
|
73
|
+
# target for data operation propagation (only set for non-snapshots, and only records non-snapshot views)
|
|
74
|
+
mutable_views: set[TableVersionHandle]
|
|
75
|
+
|
|
77
76
|
# contains complete history of columns, incl dropped ones
|
|
78
77
|
cols: list[Column]
|
|
79
78
|
# contains only user-facing (named) columns visible in this version
|
|
80
79
|
cols_by_name: dict[str, Column]
|
|
81
80
|
# contains only columns visible in this version, both system and user
|
|
82
81
|
cols_by_id: dict[int, Column]
|
|
83
|
-
# needed for _create_tbl_md()
|
|
84
|
-
idx_md: dict[int, schema.IndexMd]
|
|
85
82
|
# contains only actively maintained indices
|
|
86
83
|
idxs_by_name: dict[str, TableVersion.IndexInfo]
|
|
87
84
|
|
|
88
85
|
external_stores: dict[str, pxt.io.ExternalStore]
|
|
89
|
-
store_tbl: 'store.StoreBase'
|
|
86
|
+
store_tbl: Optional['store.StoreBase']
|
|
87
|
+
|
|
88
|
+
# used by Catalog to invalidate cached instances at the end of a transaction;
|
|
89
|
+
# True if this instance reflects the state of stored metadata in the context of this transaction and
|
|
90
|
+
# it is the instance cached in Catalog
|
|
91
|
+
is_validated: bool
|
|
90
92
|
|
|
91
93
|
@dataclasses.dataclass
|
|
92
94
|
class IndexInfo:
|
|
@@ -106,21 +108,15 @@ class TableVersion:
|
|
|
106
108
|
mutable_views: list[TableVersionHandle],
|
|
107
109
|
base_path: Optional[pxt.catalog.TableVersionPath] = None,
|
|
108
110
|
base: Optional[TableVersionHandle] = None,
|
|
109
|
-
# base_store_tbl: Optional['store.StoreBase'] = None,
|
|
110
111
|
):
|
|
112
|
+
self.is_validated = True # a freshly constructed instance is always valid
|
|
111
113
|
self.id = id
|
|
112
|
-
self.
|
|
113
|
-
self.
|
|
114
|
+
self._tbl_md = copy.deepcopy(tbl_md)
|
|
115
|
+
self._schema_version_md = copy.deepcopy(schema_version_md)
|
|
114
116
|
self.effective_version = effective_version
|
|
115
|
-
self.version = tbl_md.current_version if effective_version is None else effective_version
|
|
116
|
-
self.is_replica = tbl_md.is_replica
|
|
117
|
-
self.comment = schema_version_md.comment
|
|
118
|
-
self.num_retained_versions = schema_version_md.num_retained_versions
|
|
119
|
-
self.schema_version = schema_version_md.schema_version
|
|
120
|
-
self.view_md = tbl_md.view_md # save this as-is, it's needed for _create_md()
|
|
121
|
-
self.media_validation = MediaValidation[schema_version_md.media_validation.upper()]
|
|
122
117
|
assert not (self.is_view and base is None)
|
|
123
118
|
self.base = base
|
|
119
|
+
self.store_tbl = None
|
|
124
120
|
|
|
125
121
|
# mutable tables need their TableVersionPath for expr eval during updates
|
|
126
122
|
from .table_version_handle import TableVersionHandle
|
|
@@ -134,22 +130,11 @@ class TableVersion:
|
|
|
134
130
|
assert base_path is not None
|
|
135
131
|
self.path = TableVersionPath(self_handle, base=base_path)
|
|
136
132
|
|
|
137
|
-
if self.is_snapshot:
|
|
138
|
-
self.next_col_id = -1
|
|
139
|
-
self.next_idx_id = -1 # TODO: can snapshots have separate indices?
|
|
140
|
-
self.next_rowid = -1
|
|
141
|
-
else:
|
|
142
|
-
assert tbl_md.current_version == self.version
|
|
143
|
-
self.next_col_id = tbl_md.next_col_id
|
|
144
|
-
self.next_idx_id = tbl_md.next_idx_id
|
|
145
|
-
self.next_rowid = tbl_md.next_row_id
|
|
146
|
-
|
|
147
133
|
# view-specific initialization
|
|
148
134
|
from pixeltable import exprs
|
|
149
135
|
|
|
150
136
|
predicate_dict = None if self.view_md is None or self.view_md.predicate is None else self.view_md.predicate
|
|
151
137
|
self.predicate = exprs.Expr.from_dict(predicate_dict) if predicate_dict is not None else None
|
|
152
|
-
self.mutable_views = mutable_views
|
|
153
138
|
|
|
154
139
|
# component view-specific initialization
|
|
155
140
|
self.iterator_cls = None
|
|
@@ -164,22 +149,26 @@ class TableVersion:
|
|
|
164
149
|
self.num_iterator_cols = len(output_schema)
|
|
165
150
|
assert tbl_md.view_md.iterator_args is not None
|
|
166
151
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
cat.add_tbl_version(self)
|
|
152
|
+
self.mutable_views = set(mutable_views)
|
|
153
|
+
assert self.is_mutable or len(self.mutable_views) == 0
|
|
170
154
|
|
|
171
|
-
# init schema after we determined whether we're a component view, and before we create the store table
|
|
172
155
|
self.cols = []
|
|
173
156
|
self.cols_by_name = {}
|
|
174
157
|
self.cols_by_id = {}
|
|
175
|
-
self.idx_md = tbl_md.index_md
|
|
176
158
|
self.idxs_by_name = {}
|
|
177
159
|
self.external_stores = {}
|
|
178
160
|
|
|
179
|
-
|
|
161
|
+
def init(self) -> None:
|
|
162
|
+
"""
|
|
163
|
+
Initialize schema-related in-memory metadata separately, now that this TableVersion instance is visible
|
|
164
|
+
in Catalog.
|
|
165
|
+
"""
|
|
166
|
+
from .catalog import Catalog
|
|
180
167
|
|
|
181
|
-
|
|
182
|
-
self.
|
|
168
|
+
assert (self.id, self.effective_version) in Catalog.get()._tbl_versions
|
|
169
|
+
self._init_schema()
|
|
170
|
+
# init external stores; this needs to happen after the schema is created
|
|
171
|
+
self._init_external_stores()
|
|
183
172
|
|
|
184
173
|
def __hash__(self) -> int:
|
|
185
174
|
return hash(self.id)
|
|
@@ -188,19 +177,7 @@ class TableVersion:
|
|
|
188
177
|
"""Create a snapshot copy of this TableVersion"""
|
|
189
178
|
assert not self.is_snapshot
|
|
190
179
|
base = self.path.base.tbl_version if self.is_view else None
|
|
191
|
-
return TableVersion(
|
|
192
|
-
self.id,
|
|
193
|
-
self._create_tbl_md(),
|
|
194
|
-
self.version,
|
|
195
|
-
self._create_schema_version_md(preceding_schema_version=0), # preceding_schema_version: dummy value
|
|
196
|
-
mutable_views=[],
|
|
197
|
-
base=base,
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
def create_handle(self) -> TableVersionHandle:
|
|
201
|
-
from .table_version_handle import TableVersionHandle
|
|
202
|
-
|
|
203
|
-
return TableVersionHandle(self.id, self.effective_version, tbl_version=self)
|
|
180
|
+
return TableVersion(self.id, self.tbl_md, self.version, self.schema_version_md, mutable_views=[], base=base)
|
|
204
181
|
|
|
205
182
|
@property
|
|
206
183
|
def versioned_name(self) -> str:
|
|
@@ -306,8 +283,19 @@ class TableVersion:
|
|
|
306
283
|
tbl_version = cls(
|
|
307
284
|
tbl_record.id, table_md, effective_version, schema_version_md, [], base_path=base_path, base=base
|
|
308
285
|
)
|
|
309
|
-
|
|
286
|
+
# TODO: break this up, so that Catalog.create_table() registers tbl_version
|
|
287
|
+
cat = pxt.catalog.Catalog.get()
|
|
288
|
+
cat._tbl_versions[tbl_record.id, effective_version] = tbl_version
|
|
289
|
+
tbl_version.init()
|
|
310
290
|
tbl_version.store_tbl.create()
|
|
291
|
+
is_mutable = not is_snapshot and not table_md.is_replica
|
|
292
|
+
if base is not None and base.get().is_mutable and is_mutable:
|
|
293
|
+
from .table_version_handle import TableVersionHandle
|
|
294
|
+
|
|
295
|
+
handle = TableVersionHandle(tbl_version.id, effective_version)
|
|
296
|
+
assert handle not in base.get().mutable_views
|
|
297
|
+
base.get().mutable_views.add(handle)
|
|
298
|
+
|
|
311
299
|
if view_md is None or not view_md.is_snapshot:
|
|
312
300
|
# add default indices, after creating the store table
|
|
313
301
|
for col in tbl_version.cols_by_name.values():
|
|
@@ -315,7 +303,7 @@ class TableVersion:
|
|
|
315
303
|
assert status is None or status.num_excs == 0
|
|
316
304
|
|
|
317
305
|
# we re-create the tbl_record here, now that we have new index metadata
|
|
318
|
-
tbl_record = schema.Table(id=tbl_id, dir_id=dir_id, md=dataclasses.asdict(tbl_version.
|
|
306
|
+
tbl_record = schema.Table(id=tbl_id, dir_id=dir_id, md=dataclasses.asdict(tbl_version.tbl_md))
|
|
319
307
|
session.add(tbl_record)
|
|
320
308
|
session.add(tbl_version_record)
|
|
321
309
|
session.add(schema_version_record)
|
|
@@ -324,18 +312,31 @@ class TableVersion:
|
|
|
324
312
|
@classmethod
|
|
325
313
|
def create_replica(cls, md: schema.FullTableMd) -> TableVersion:
|
|
326
314
|
tbl_id = UUID(md.tbl_md.tbl_id)
|
|
315
|
+
_logger.info(f'Creating replica table version {tbl_id}:{md.version_md.version}.')
|
|
327
316
|
view_md = md.tbl_md.view_md
|
|
328
317
|
base_path = pxt.catalog.TableVersionPath.from_md(view_md.base_versions) if view_md is not None else None
|
|
329
318
|
base = base_path.tbl_version if base_path is not None else None
|
|
330
319
|
tbl_version = cls(
|
|
331
320
|
tbl_id, md.tbl_md, md.version_md.version, md.schema_version_md, [], base_path=base_path, base=base
|
|
332
321
|
)
|
|
322
|
+
cat = pxt.catalog.Catalog.get()
|
|
323
|
+
cat._tbl_versions[tbl_version.id, tbl_version.effective_version] = tbl_version
|
|
324
|
+
tbl_version.init()
|
|
333
325
|
tbl_version.store_tbl.create()
|
|
326
|
+
tbl_version.store_tbl.ensure_columns_exist(col for col in tbl_version.cols if col.is_stored)
|
|
334
327
|
return tbl_version
|
|
335
328
|
|
|
336
329
|
def drop(self) -> None:
|
|
337
330
|
from .catalog import Catalog
|
|
338
331
|
|
|
332
|
+
if self.is_view and self.is_mutable:
|
|
333
|
+
# update mutable_views
|
|
334
|
+
from .table_version_handle import TableVersionHandle
|
|
335
|
+
|
|
336
|
+
assert self.base is not None
|
|
337
|
+
if self.base.get().is_mutable:
|
|
338
|
+
self.base.get().mutable_views.remove(TableVersionHandle.create(self))
|
|
339
|
+
|
|
339
340
|
cat = Catalog.get()
|
|
340
341
|
# delete this table and all associated data
|
|
341
342
|
MediaStore.delete(self.id)
|
|
@@ -345,24 +346,24 @@ class TableVersion:
|
|
|
345
346
|
# de-register table version from catalog
|
|
346
347
|
cat.remove_tbl_version(self)
|
|
347
348
|
|
|
348
|
-
def _init_schema(self
|
|
349
|
+
def _init_schema(self) -> None:
|
|
349
350
|
# create columns first, so the indices can reference them
|
|
350
|
-
self._init_cols(
|
|
351
|
+
self._init_cols()
|
|
351
352
|
if not self.is_snapshot:
|
|
352
|
-
self._init_idxs(
|
|
353
|
+
self._init_idxs()
|
|
353
354
|
# create the sa schema only after creating the columns and indices
|
|
354
355
|
self._init_sa_schema()
|
|
355
356
|
|
|
356
|
-
def _init_cols(self
|
|
357
|
+
def _init_cols(self) -> None:
|
|
357
358
|
"""Initialize self.cols with the columns visible in our effective version"""
|
|
358
359
|
self.cols = []
|
|
359
360
|
self.cols_by_name = {}
|
|
360
361
|
self.cols_by_id = {}
|
|
361
362
|
# Sort columns in column_md by the position specified in col_md.id to guarantee that all references
|
|
362
363
|
# point backward.
|
|
363
|
-
sorted_column_md = sorted(tbl_md.column_md.values(), key=lambda item: item.id)
|
|
364
|
+
sorted_column_md = sorted(self.tbl_md.column_md.values(), key=lambda item: item.id)
|
|
364
365
|
for col_md in sorted_column_md:
|
|
365
|
-
schema_col_md = schema_version_md.columns.get(col_md.id)
|
|
366
|
+
schema_col_md = self.schema_version_md.columns.get(col_md.id)
|
|
366
367
|
col_name = schema_col_md.name if schema_col_md is not None else None
|
|
367
368
|
media_val = (
|
|
368
369
|
MediaValidation[schema_col_md.media_validation.upper()]
|
|
@@ -380,7 +381,7 @@ class TableVersion:
|
|
|
380
381
|
schema_version_drop=col_md.schema_version_drop,
|
|
381
382
|
value_expr_dict=col_md.value_expr,
|
|
382
383
|
)
|
|
383
|
-
col.tbl = self
|
|
384
|
+
col.tbl = self
|
|
384
385
|
self.cols.append(col)
|
|
385
386
|
|
|
386
387
|
# populate the lookup structures before Expr.from_dict()
|
|
@@ -399,12 +400,12 @@ class TableVersion:
|
|
|
399
400
|
if not self.is_snapshot and col_md.value_expr is not None:
|
|
400
401
|
self._record_refd_columns(col)
|
|
401
402
|
|
|
402
|
-
def _init_idxs(self
|
|
403
|
-
self.idx_md = tbl_md.index_md
|
|
403
|
+
def _init_idxs(self) -> None:
|
|
404
|
+
# self.idx_md = tbl_md.index_md
|
|
404
405
|
self.idxs_by_name = {}
|
|
405
406
|
import pixeltable.index as index_module
|
|
406
407
|
|
|
407
|
-
for md in tbl_md.index_md.values():
|
|
408
|
+
for md in self.tbl_md.index_md.values():
|
|
408
409
|
if md.schema_version_add > self.schema_version or (
|
|
409
410
|
md.schema_version_drop is not None and md.schema_version_drop <= self.schema_version
|
|
410
411
|
):
|
|
@@ -439,28 +440,32 @@ class TableVersion:
|
|
|
439
440
|
else:
|
|
440
441
|
self.store_tbl = StoreTable(self)
|
|
441
442
|
|
|
442
|
-
def
|
|
443
|
-
self, timestamp: float, update_tbl_version: bool = True, preceding_schema_version: Optional[int] = None
|
|
444
|
-
) -> None:
|
|
443
|
+
def _write_md(self, new_version: bool, new_version_ts: float, new_schema_version: bool) -> None:
|
|
445
444
|
"""Writes table metadata to the database.
|
|
446
445
|
|
|
447
446
|
Args:
|
|
448
447
|
timestamp: timestamp of the change
|
|
449
|
-
conn: database connection to use
|
|
450
448
|
update_tbl_version: if `True`, will also write `TableVersion` metadata
|
|
451
449
|
preceding_schema_version: if specified, will also write `TableSchemaVersion` metadata, recording the
|
|
452
450
|
specified preceding schema version
|
|
453
451
|
"""
|
|
454
|
-
assert update_tbl_version or preceding_schema_version is None
|
|
455
452
|
from pixeltable.catalog import Catalog
|
|
456
453
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
454
|
+
version_md: Optional[schema.TableVersionMd] = (
|
|
455
|
+
schema.TableVersionMd(
|
|
456
|
+
tbl_id=str(self.id),
|
|
457
|
+
created_at=new_version_ts,
|
|
458
|
+
version=self.version,
|
|
459
|
+
schema_version=self.schema_version,
|
|
460
|
+
additional_md={},
|
|
461
|
+
)
|
|
462
|
+
if new_version
|
|
463
|
+
else None
|
|
461
464
|
)
|
|
462
465
|
|
|
463
|
-
Catalog.get().store_tbl_md(
|
|
466
|
+
Catalog.get().store_tbl_md(
|
|
467
|
+
self.id, self._tbl_md, version_md, self._schema_version_md if new_schema_version else None
|
|
468
|
+
)
|
|
464
469
|
|
|
465
470
|
def ensure_md_loaded(self) -> None:
|
|
466
471
|
"""Ensure that table metadata is loaded."""
|
|
@@ -474,10 +479,10 @@ class TableVersion:
|
|
|
474
479
|
def add_index(self, col: Column, idx_name: Optional[str], idx: index.IndexBase) -> UpdateStatus:
|
|
475
480
|
# we're creating a new schema version
|
|
476
481
|
self.version += 1
|
|
477
|
-
preceding_schema_version = self.schema_version
|
|
482
|
+
self.preceding_schema_version = self.schema_version
|
|
478
483
|
self.schema_version = self.version
|
|
479
484
|
status = self._add_index(col, idx_name, idx)
|
|
480
|
-
self.
|
|
485
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
481
486
|
_logger.info(f'Added index {idx_name} on column {col.name} to table {self.name}')
|
|
482
487
|
return status
|
|
483
488
|
|
|
@@ -522,7 +527,7 @@ class TableVersion:
|
|
|
522
527
|
schema_version_drop=None,
|
|
523
528
|
records_errors=idx.records_value_errors(),
|
|
524
529
|
)
|
|
525
|
-
val_col.tbl = self
|
|
530
|
+
val_col.tbl = self
|
|
526
531
|
val_col.col_type = val_col.col_type.copy(nullable=True)
|
|
527
532
|
self.next_col_id += 1
|
|
528
533
|
|
|
@@ -536,7 +541,7 @@ class TableVersion:
|
|
|
536
541
|
schema_version_drop=None,
|
|
537
542
|
records_errors=False,
|
|
538
543
|
)
|
|
539
|
-
undo_col.tbl = self
|
|
544
|
+
undo_col.tbl = self
|
|
540
545
|
undo_col.col_type = undo_col.col_type.copy(nullable=True)
|
|
541
546
|
self.next_col_id += 1
|
|
542
547
|
return val_col, undo_col
|
|
@@ -551,7 +556,7 @@ class TableVersion:
|
|
|
551
556
|
idx_name = f'idx{idx_id}'
|
|
552
557
|
else:
|
|
553
558
|
assert is_valid_identifier(idx_name)
|
|
554
|
-
assert idx_name not in [i.name for i in self.
|
|
559
|
+
assert idx_name not in [i.name for i in self._tbl_md.index_md.values()]
|
|
555
560
|
# create and register the index metadata
|
|
556
561
|
idx_cls = type(idx)
|
|
557
562
|
idx_md = schema.IndexMd(
|
|
@@ -567,7 +572,7 @@ class TableVersion:
|
|
|
567
572
|
init_args=idx.as_dict(),
|
|
568
573
|
)
|
|
569
574
|
idx_info = self.IndexInfo(id=idx_id, name=idx_name, idx=idx, col=col, val_col=val_col, undo_col=undo_col)
|
|
570
|
-
self.
|
|
575
|
+
self._tbl_md.index_md[idx_id] = idx_md
|
|
571
576
|
self.idxs_by_name[idx_name] = idx_info
|
|
572
577
|
try:
|
|
573
578
|
idx.create_index(self._store_idx_name(idx_id), val_col)
|
|
@@ -576,7 +581,7 @@ class TableVersion:
|
|
|
576
581
|
def cleanup_index() -> None:
|
|
577
582
|
"""Delete the newly added in-memory index structure"""
|
|
578
583
|
del self.idxs_by_name[idx_name]
|
|
579
|
-
del self.
|
|
584
|
+
del self._tbl_md.index_md[idx_id]
|
|
580
585
|
self.next_idx_id = idx_id
|
|
581
586
|
|
|
582
587
|
# Run cleanup only if there has been an exception; otherwise, skip cleanup.
|
|
@@ -594,47 +599,48 @@ class TableVersion:
|
|
|
594
599
|
|
|
595
600
|
def drop_index(self, idx_id: int) -> None:
|
|
596
601
|
assert not self.is_snapshot
|
|
597
|
-
assert idx_id in self.
|
|
602
|
+
assert idx_id in self._tbl_md.index_md
|
|
598
603
|
|
|
599
604
|
# we're creating a new schema version
|
|
600
605
|
self.version += 1
|
|
601
|
-
preceding_schema_version = self.schema_version
|
|
606
|
+
self.preceding_schema_version = self.schema_version
|
|
602
607
|
self.schema_version = self.version
|
|
603
|
-
idx_md = self.
|
|
608
|
+
idx_md = self._tbl_md.index_md[idx_id]
|
|
604
609
|
idx_md.schema_version_drop = self.schema_version
|
|
605
610
|
assert idx_md.name in self.idxs_by_name
|
|
606
611
|
idx_info = self.idxs_by_name[idx_md.name]
|
|
607
612
|
# remove this index entry from the active indexes (in memory)
|
|
608
613
|
# and the index metadata (in persistent table metadata)
|
|
614
|
+
# TODO: this is wrong, it breaks revert()
|
|
609
615
|
del self.idxs_by_name[idx_md.name]
|
|
610
|
-
del self.
|
|
616
|
+
del self._tbl_md.index_md[idx_id]
|
|
611
617
|
|
|
612
618
|
self._drop_columns([idx_info.val_col, idx_info.undo_col])
|
|
613
|
-
self.
|
|
619
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
614
620
|
_logger.info(f'Dropped index {idx_md.name} on table {self.name}')
|
|
615
621
|
|
|
616
622
|
def add_columns(
|
|
617
623
|
self, cols: Iterable[Column], print_stats: bool, on_error: Literal['abort', 'ignore']
|
|
618
624
|
) -> UpdateStatus:
|
|
619
|
-
"""Adds
|
|
625
|
+
"""Adds columns to the table."""
|
|
620
626
|
assert not self.is_snapshot
|
|
621
|
-
assert all(is_valid_identifier(col.name) for col in cols)
|
|
627
|
+
assert all(is_valid_identifier(col.name) for col in cols if col.name is not None)
|
|
622
628
|
assert all(col.stored is not None for col in cols)
|
|
623
|
-
assert all(col.name not in self.cols_by_name for col in cols)
|
|
629
|
+
assert all(col.name not in self.cols_by_name for col in cols if col.name is not None)
|
|
624
630
|
for col in cols:
|
|
625
|
-
col.tbl = self
|
|
631
|
+
col.tbl = self
|
|
626
632
|
col.id = self.next_col_id
|
|
627
633
|
self.next_col_id += 1
|
|
628
634
|
|
|
629
635
|
# we're creating a new schema version
|
|
630
636
|
self.version += 1
|
|
631
|
-
preceding_schema_version = self.schema_version
|
|
637
|
+
self.preceding_schema_version = self.schema_version
|
|
632
638
|
self.schema_version = self.version
|
|
633
639
|
index_cols: dict[Column, tuple[index.BtreeIndex, Column, Column]] = {}
|
|
634
640
|
all_cols: list[Column] = []
|
|
635
641
|
for col in cols:
|
|
636
642
|
all_cols.append(col)
|
|
637
|
-
if self._is_btree_indexable(col):
|
|
643
|
+
if col.name is not None and self._is_btree_indexable(col):
|
|
638
644
|
idx = index.BtreeIndex(col)
|
|
639
645
|
val_col, undo_col = self._create_index_columns(idx)
|
|
640
646
|
index_cols[col] = (idx, val_col, undo_col)
|
|
@@ -642,10 +648,10 @@ class TableVersion:
|
|
|
642
648
|
all_cols.append(undo_col)
|
|
643
649
|
# Add all columns
|
|
644
650
|
status = self._add_columns(all_cols, print_stats=print_stats, on_error=on_error)
|
|
645
|
-
# Create indices and their
|
|
651
|
+
# Create indices and their md records
|
|
646
652
|
for col, (idx, val_col, undo_col) in index_cols.items():
|
|
647
653
|
self._create_index(col, val_col, undo_col, idx_name=None, idx=idx)
|
|
648
|
-
self.
|
|
654
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
649
655
|
_logger.info(f'Added columns {[col.name for col in cols]} to table {self.name}, new version: {self.version}')
|
|
650
656
|
|
|
651
657
|
msg = (
|
|
@@ -683,6 +689,23 @@ class TableVersion:
|
|
|
683
689
|
col.check_value_expr()
|
|
684
690
|
self._record_refd_columns(col)
|
|
685
691
|
|
|
692
|
+
# also add to stored md
|
|
693
|
+
self._tbl_md.column_md[col.id] = schema.ColumnMd(
|
|
694
|
+
id=col.id,
|
|
695
|
+
col_type=col.col_type.as_dict(),
|
|
696
|
+
is_pk=col.is_pk,
|
|
697
|
+
schema_version_add=col.schema_version_add,
|
|
698
|
+
schema_version_drop=col.schema_version_drop,
|
|
699
|
+
value_expr=col.value_expr.as_dict() if col.value_expr is not None else None,
|
|
700
|
+
stored=col.stored,
|
|
701
|
+
)
|
|
702
|
+
if col.name is not None:
|
|
703
|
+
self._schema_version_md.columns[col.id] = schema.SchemaColumn(
|
|
704
|
+
name=col.name,
|
|
705
|
+
pos=len(self.cols_by_name),
|
|
706
|
+
media_validation=col._media_validation.name.lower() if col._media_validation is not None else None,
|
|
707
|
+
)
|
|
708
|
+
|
|
686
709
|
if col.is_stored:
|
|
687
710
|
self.store_tbl.add_column(col)
|
|
688
711
|
|
|
@@ -729,7 +752,7 @@ class TableVersion:
|
|
|
729
752
|
num_rows=row_count,
|
|
730
753
|
num_computed_values=row_count,
|
|
731
754
|
num_excs=num_excs,
|
|
732
|
-
cols_with_excs=[f'{col.tbl.
|
|
755
|
+
cols_with_excs=[f'{col.tbl.name}.{col.name}' for col in cols_with_excs if col.name is not None],
|
|
733
756
|
)
|
|
734
757
|
|
|
735
758
|
def drop_column(self, col: Column) -> None:
|
|
@@ -739,7 +762,7 @@ class TableVersion:
|
|
|
739
762
|
|
|
740
763
|
# we're creating a new schema version
|
|
741
764
|
self.version += 1
|
|
742
|
-
preceding_schema_version = self.schema_version
|
|
765
|
+
self.preceding_schema_version = self.schema_version
|
|
743
766
|
self.schema_version = self.version
|
|
744
767
|
|
|
745
768
|
# drop this column and all dependent index columns and indices
|
|
@@ -749,15 +772,17 @@ class TableVersion:
|
|
|
749
772
|
if idx_info.col != col:
|
|
750
773
|
continue
|
|
751
774
|
dropped_cols.extend([idx_info.val_col, idx_info.undo_col])
|
|
752
|
-
idx_md = self.
|
|
775
|
+
idx_md = self._tbl_md.index_md[idx_info.id]
|
|
753
776
|
idx_md.schema_version_drop = self.schema_version
|
|
754
777
|
assert idx_md.name in self.idxs_by_name
|
|
755
778
|
dropped_idx_names.append(idx_md.name)
|
|
779
|
+
|
|
756
780
|
# update idxs_by_name
|
|
757
781
|
for idx_name in dropped_idx_names:
|
|
758
782
|
del self.idxs_by_name[idx_name]
|
|
783
|
+
|
|
759
784
|
self._drop_columns(dropped_cols)
|
|
760
|
-
self.
|
|
785
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
761
786
|
_logger.info(f'Dropped column {col.name} from table {self.name}, new version: {self.version}')
|
|
762
787
|
|
|
763
788
|
def _drop_columns(self, cols: Iterable[Column]) -> None:
|
|
@@ -778,6 +803,14 @@ class TableVersion:
|
|
|
778
803
|
del self.cols_by_name[col.name]
|
|
779
804
|
assert col.id in self.cols_by_id
|
|
780
805
|
del self.cols_by_id[col.id]
|
|
806
|
+
# update stored md
|
|
807
|
+
self._tbl_md.column_md[col.id].schema_version_drop = col.schema_version_drop
|
|
808
|
+
if col.name is not None:
|
|
809
|
+
del self._schema_version_md.columns[col.id]
|
|
810
|
+
|
|
811
|
+
# update positions
|
|
812
|
+
for pos, schema_col in enumerate(self._schema_version_md.columns.values()):
|
|
813
|
+
schema_col.pos = pos
|
|
781
814
|
|
|
782
815
|
self.store_tbl.create_sa_tbl()
|
|
783
816
|
|
|
@@ -794,13 +827,14 @@ class TableVersion:
|
|
|
794
827
|
del self.cols_by_name[old_name]
|
|
795
828
|
col.name = new_name
|
|
796
829
|
self.cols_by_name[new_name] = col
|
|
830
|
+
self._schema_version_md.columns[col.id].name = new_name
|
|
797
831
|
|
|
798
832
|
# we're creating a new schema version
|
|
799
833
|
self.version += 1
|
|
800
|
-
preceding_schema_version = self.schema_version
|
|
834
|
+
self.preceding_schema_version = self.schema_version
|
|
801
835
|
self.schema_version = self.version
|
|
802
836
|
|
|
803
|
-
self.
|
|
837
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
804
838
|
_logger.info(f'Renamed column {old_name} to {new_name} in table {self.name}, new version: {self.version}')
|
|
805
839
|
|
|
806
840
|
def set_comment(self, new_comment: Optional[str]) -> None:
|
|
@@ -819,9 +853,9 @@ class TableVersion:
|
|
|
819
853
|
def _create_schema_version(self) -> None:
|
|
820
854
|
# we're creating a new schema version
|
|
821
855
|
self.version += 1
|
|
822
|
-
preceding_schema_version = self.schema_version
|
|
856
|
+
self.preceding_schema_version = self.schema_version
|
|
823
857
|
self.schema_version = self.version
|
|
824
|
-
self.
|
|
858
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
825
859
|
_logger.info(f'[{self.name}] Updating table schema to version: {self.version}')
|
|
826
860
|
|
|
827
861
|
def insert(
|
|
@@ -836,7 +870,7 @@ class TableVersion:
|
|
|
836
870
|
"""
|
|
837
871
|
from pixeltable.plan import Planner
|
|
838
872
|
|
|
839
|
-
assert self.is_insertable
|
|
873
|
+
assert self.is_insertable
|
|
840
874
|
assert (rows is None) != (df is None) # Exactly one must be specified
|
|
841
875
|
if rows is not None:
|
|
842
876
|
plan = Planner.create_insert_plan(self, rows, ignore_errors=not fail_on_exception)
|
|
@@ -846,8 +880,8 @@ class TableVersion:
|
|
|
846
880
|
# this is a base table; we generate rowids during the insert
|
|
847
881
|
def rowids() -> Iterator[int]:
|
|
848
882
|
while True:
|
|
849
|
-
rowid = self.
|
|
850
|
-
self.
|
|
883
|
+
rowid = self.next_row_id
|
|
884
|
+
self.next_row_id += 1
|
|
851
885
|
yield rowid
|
|
852
886
|
|
|
853
887
|
return self._insert(plan, time.time(), print_stats=print_stats, rowids=rowids(), abort_on_exc=fail_on_exception)
|
|
@@ -872,7 +906,7 @@ class TableVersion:
|
|
|
872
906
|
result.num_excs = num_excs
|
|
873
907
|
result.num_computed_values += exec_plan.ctx.num_computed_exprs * num_rows
|
|
874
908
|
result.cols_with_excs = [f'{self.name}.{self.cols_by_id[cid].name}' for cid in cols_with_excs]
|
|
875
|
-
self.
|
|
909
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=False)
|
|
876
910
|
|
|
877
911
|
# update views
|
|
878
912
|
for view in self.mutable_views:
|
|
@@ -1036,13 +1070,13 @@ class TableVersion:
|
|
|
1036
1070
|
self.store_tbl.delete_rows(
|
|
1037
1071
|
self.version, base_versions=base_versions, match_on_vmin=True, where_clause=where_clause
|
|
1038
1072
|
)
|
|
1039
|
-
self.
|
|
1073
|
+
self._write_md(new_version=True, new_version_ts=timestamp, new_schema_version=False)
|
|
1040
1074
|
|
|
1041
1075
|
if cascade:
|
|
1042
1076
|
base_versions = [None if plan is None else self.version, *base_versions] # don't update in place
|
|
1043
1077
|
# propagate to views
|
|
1044
1078
|
for view in self.mutable_views:
|
|
1045
|
-
recomputed_cols = [col for col in recomputed_view_cols if col.tbl == view]
|
|
1079
|
+
recomputed_cols = [col for col in recomputed_view_cols if col.tbl.id == view.id]
|
|
1046
1080
|
plan = None
|
|
1047
1081
|
if len(recomputed_cols) > 0:
|
|
1048
1082
|
from pixeltable.plan import Planner
|
|
@@ -1063,7 +1097,7 @@ class TableVersion:
|
|
|
1063
1097
|
Args:
|
|
1064
1098
|
where: a predicate to filter rows to delete.
|
|
1065
1099
|
"""
|
|
1066
|
-
assert self.is_insertable
|
|
1100
|
+
assert self.is_insertable
|
|
1067
1101
|
from pixeltable.exprs import Expr
|
|
1068
1102
|
from pixeltable.plan import Planner
|
|
1069
1103
|
|
|
@@ -1091,14 +1125,22 @@ class TableVersion:
|
|
|
1091
1125
|
Returns:
|
|
1092
1126
|
number of deleted rows
|
|
1093
1127
|
"""
|
|
1128
|
+
# print(f'calling sql_expr()')
|
|
1094
1129
|
sql_where_clause = where.sql_expr(exprs.SqlElementCache()) if where is not None else None
|
|
1130
|
+
# #print(f'sql_where_clause={str(sql_where_clause) if sql_where_clause is not None else None}')
|
|
1131
|
+
# sql_cols: list[sql.Column] = []
|
|
1132
|
+
# def collect_cols(col) -> None:
|
|
1133
|
+
# sql_cols.append(col)
|
|
1134
|
+
# sql.sql.visitors.traverse(sql_where_clause, {}, {'column': collect_cols})
|
|
1135
|
+
# x = [f'{str(c)}:{hash(c)}:{id(c.table)}' for c in sql_cols]
|
|
1136
|
+
# print(f'where_clause cols: {x}')
|
|
1095
1137
|
num_rows = self.store_tbl.delete_rows(
|
|
1096
1138
|
self.version + 1, base_versions=base_versions, match_on_vmin=False, where_clause=sql_where_clause
|
|
1097
1139
|
)
|
|
1098
1140
|
if num_rows > 0:
|
|
1099
1141
|
# we're creating a new version
|
|
1100
1142
|
self.version += 1
|
|
1101
|
-
self.
|
|
1143
|
+
self._write_md(new_version=True, new_version_ts=timestamp, new_schema_version=False)
|
|
1102
1144
|
for view in self.mutable_views:
|
|
1103
1145
|
num_rows += view.get().propagate_delete(
|
|
1104
1146
|
where=None, base_versions=[self.version, *base_versions], timestamp=timestamp
|
|
@@ -1112,17 +1154,13 @@ class TableVersion:
|
|
|
1112
1154
|
raise excs.Error('Cannot revert version 0')
|
|
1113
1155
|
self._revert()
|
|
1114
1156
|
|
|
1115
|
-
def _delete_column(self, col: Column) -> None:
|
|
1116
|
-
"""Physically remove the column from the schema and the store table"""
|
|
1117
|
-
if col.is_stored:
|
|
1118
|
-
self.store_tbl.drop_column(col)
|
|
1119
|
-
self.cols.remove(col)
|
|
1120
|
-
if col.name is not None:
|
|
1121
|
-
del self.cols_by_name[col.name]
|
|
1122
|
-
del self.cols_by_id[col.id]
|
|
1123
|
-
|
|
1124
1157
|
def _revert(self) -> None:
|
|
1125
|
-
"""
|
|
1158
|
+
"""
|
|
1159
|
+
Reverts the stored metadata for this table version and propagates to views.
|
|
1160
|
+
|
|
1161
|
+
Doesn't attempt to revert the in-memory metadata, but instead invalidates this TableVersion instance
|
|
1162
|
+
and relies on Catalog to reload it
|
|
1163
|
+
"""
|
|
1126
1164
|
conn = Env.get().conn
|
|
1127
1165
|
# make sure we don't have a snapshot referencing this version
|
|
1128
1166
|
# (unclear how to express this with sqlalchemy)
|
|
@@ -1155,109 +1193,206 @@ class TableVersion:
|
|
|
1155
1193
|
stmt = sql.update(self.store_tbl.sa_tbl).values(set_clause).where(self.store_tbl.sa_tbl.c.v_max == self.version)
|
|
1156
1194
|
conn.execute(stmt)
|
|
1157
1195
|
|
|
1158
|
-
# revert schema changes
|
|
1196
|
+
# revert schema changes:
|
|
1197
|
+
# - undo changes to self._tbl_md and write that back
|
|
1198
|
+
# - delete newly-added TableVersion/TableSchemaVersion records
|
|
1159
1199
|
if self.version == self.schema_version:
|
|
1160
|
-
# delete newly-added columns
|
|
1200
|
+
# physically delete newly-added columns and remove them from the stored md
|
|
1161
1201
|
added_cols = [col for col in self.cols if col.schema_version_add == self.schema_version]
|
|
1162
1202
|
if len(added_cols) > 0:
|
|
1163
|
-
next_col_id = min(col.id for col in added_cols)
|
|
1203
|
+
self._tbl_md.next_col_id = min(col.id for col in added_cols)
|
|
1164
1204
|
for col in added_cols:
|
|
1165
|
-
|
|
1166
|
-
|
|
1205
|
+
if col.is_stored:
|
|
1206
|
+
self.store_tbl.drop_column(col)
|
|
1207
|
+
del self._tbl_md.column_md[col.id]
|
|
1167
1208
|
|
|
1168
1209
|
# remove newly-added indices from the lookup structures
|
|
1169
1210
|
# (the value and undo columns got removed in the preceding step)
|
|
1170
|
-
added_idx_md = [md for md in self.
|
|
1211
|
+
added_idx_md = [md for md in self._tbl_md.index_md.values() if md.schema_version_add == self.schema_version]
|
|
1171
1212
|
if len(added_idx_md) > 0:
|
|
1172
|
-
next_idx_id = min(md.id for md in added_idx_md)
|
|
1213
|
+
self._tbl_md.next_idx_id = min(md.id for md in added_idx_md)
|
|
1173
1214
|
for md in added_idx_md:
|
|
1174
|
-
|
|
1175
|
-
del self.
|
|
1176
|
-
self.next_idx_id = next_idx_id
|
|
1215
|
+
# TODO: drop the index
|
|
1216
|
+
del self._tbl_md.index_md[md.id]
|
|
1177
1217
|
|
|
1178
1218
|
# make newly-dropped columns visible again
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1219
|
+
dropped_col_md = [
|
|
1220
|
+
md for md in self._tbl_md.column_md.values() if md.schema_version_drop == self.schema_version
|
|
1221
|
+
]
|
|
1222
|
+
for col_md in dropped_col_md:
|
|
1223
|
+
col_md.schema_version_drop = None
|
|
1182
1224
|
|
|
1183
1225
|
# make newly-dropped indices visible again
|
|
1184
|
-
dropped_idx_md = [
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
# we need to determine the preceding schema version and reload the schema
|
|
1190
|
-
schema_version_md_dict = (
|
|
1191
|
-
session.query(schema.TableSchemaVersion.md)
|
|
1192
|
-
.where(schema.TableSchemaVersion.tbl_id == self.id)
|
|
1193
|
-
.where(schema.TableSchemaVersion.schema_version == self.schema_version)
|
|
1194
|
-
.scalar()
|
|
1195
|
-
)
|
|
1196
|
-
preceding_schema_version = schema_version_md_dict['preceding_schema_version']
|
|
1197
|
-
preceding_schema_version_md_dict = (
|
|
1198
|
-
session.query(schema.TableSchemaVersion.md)
|
|
1199
|
-
.where(schema.TableSchemaVersion.tbl_id == self.id)
|
|
1200
|
-
.where(schema.TableSchemaVersion.schema_version == preceding_schema_version)
|
|
1201
|
-
.scalar()
|
|
1202
|
-
)
|
|
1203
|
-
preceding_schema_version_md = schema.md_from_dict(
|
|
1204
|
-
schema.TableSchemaVersionMd, preceding_schema_version_md_dict
|
|
1205
|
-
)
|
|
1206
|
-
tbl_md = self._create_tbl_md()
|
|
1207
|
-
self._init_schema(tbl_md, preceding_schema_version_md)
|
|
1226
|
+
dropped_idx_md = [
|
|
1227
|
+
md for md in self._tbl_md.index_md.values() if md.schema_version_drop == self.schema_version
|
|
1228
|
+
]
|
|
1229
|
+
for idx_md in dropped_idx_md:
|
|
1230
|
+
idx_md.schema_version_drop = None
|
|
1208
1231
|
|
|
1209
1232
|
conn.execute(
|
|
1210
1233
|
sql.delete(schema.TableSchemaVersion.__table__)
|
|
1211
1234
|
.where(schema.TableSchemaVersion.tbl_id == self.id)
|
|
1212
1235
|
.where(schema.TableSchemaVersion.schema_version == self.schema_version)
|
|
1213
1236
|
)
|
|
1214
|
-
self.
|
|
1215
|
-
self.comment = preceding_schema_version_md.comment
|
|
1216
|
-
self.num_retained_versions = preceding_schema_version_md.num_retained_versions
|
|
1237
|
+
self._tbl_md.current_schema_version = self._schema_version_md.preceding_schema_version
|
|
1217
1238
|
|
|
1218
1239
|
conn.execute(
|
|
1219
1240
|
sql.delete(schema.TableVersion.__table__)
|
|
1220
1241
|
.where(schema.TableVersion.tbl_id == self.id)
|
|
1221
1242
|
.where(schema.TableVersion.version == self.version)
|
|
1222
1243
|
)
|
|
1244
|
+
|
|
1223
1245
|
self.version -= 1
|
|
1224
|
-
|
|
1225
|
-
sql.update(schema.Table.__table__)
|
|
1226
|
-
.values({schema.Table.md: dataclasses.asdict(self._create_tbl_md())})
|
|
1227
|
-
.where(schema.Table.id == self.id)
|
|
1228
|
-
)
|
|
1246
|
+
self._write_md(new_version=False, new_version_ts=0, new_schema_version=False)
|
|
1229
1247
|
|
|
1230
1248
|
# propagate to views
|
|
1249
|
+
views_str = ', '.join([str(v.id) for v in self.mutable_views])
|
|
1250
|
+
print(f'revert(): mutable_views={views_str}')
|
|
1231
1251
|
for view in self.mutable_views:
|
|
1232
1252
|
view.get()._revert()
|
|
1253
|
+
|
|
1254
|
+
# force reload on next operation
|
|
1255
|
+
self.is_validated = False
|
|
1256
|
+
pxt.catalog.Catalog.get().remove_tbl_version(self)
|
|
1233
1257
|
_logger.info(f'TableVersion {self.name}: reverted to version {self.version}')
|
|
1234
1258
|
|
|
1235
|
-
def _init_external_stores(self
|
|
1236
|
-
for store_md in tbl_md.external_stores:
|
|
1259
|
+
def _init_external_stores(self) -> None:
|
|
1260
|
+
for store_md in self.tbl_md.external_stores:
|
|
1237
1261
|
store_cls = resolve_symbol(store_md['class'])
|
|
1238
1262
|
assert isinstance(store_cls, type) and issubclass(store_cls, pxt.io.ExternalStore)
|
|
1239
1263
|
store = store_cls.from_dict(store_md['md'])
|
|
1240
1264
|
self.external_stores[store.name] = store
|
|
1241
1265
|
|
|
1242
1266
|
def link_external_store(self, store: pxt.io.ExternalStore) -> None:
|
|
1243
|
-
|
|
1267
|
+
self.version += 1
|
|
1268
|
+
self.preceding_schema_version = self.schema_version
|
|
1269
|
+
self.schema_version = self.version
|
|
1270
|
+
|
|
1244
1271
|
self.external_stores[store.name] = store
|
|
1245
|
-
self.
|
|
1272
|
+
self._tbl_md.external_stores.append(
|
|
1273
|
+
{'class': f'{type(store).__module__}.{type(store).__qualname__}', 'md': store.as_dict()}
|
|
1274
|
+
)
|
|
1275
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
1276
|
+
|
|
1277
|
+
def unlink_external_store(self, store: pxt.io.ExternalStore) -> None:
|
|
1278
|
+
del self.external_stores[store.name]
|
|
1279
|
+
self.version += 1
|
|
1280
|
+
self.preceding_schema_version = self.schema_version
|
|
1281
|
+
self.schema_version = self.version
|
|
1282
|
+
idx = next(i for i, store_md in enumerate(self._tbl_md.external_stores) if store_md['md']['name'] == store.name)
|
|
1283
|
+
self._tbl_md.external_stores.pop(idx)
|
|
1284
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
1285
|
+
|
|
1286
|
+
@property
|
|
1287
|
+
def tbl_md(self) -> schema.TableMd:
|
|
1288
|
+
return self._tbl_md
|
|
1289
|
+
|
|
1290
|
+
@property
|
|
1291
|
+
def schema_version_md(self) -> schema.TableSchemaVersionMd:
|
|
1292
|
+
return self._schema_version_md
|
|
1293
|
+
|
|
1294
|
+
@property
|
|
1295
|
+
def view_md(self) -> Optional[schema.ViewMd]:
|
|
1296
|
+
return self._tbl_md.view_md
|
|
1297
|
+
|
|
1298
|
+
@property
|
|
1299
|
+
def name(self) -> str:
|
|
1300
|
+
return self._tbl_md.name
|
|
1301
|
+
|
|
1302
|
+
@property
|
|
1303
|
+
def user(self) -> Optional[str]:
|
|
1304
|
+
return self._tbl_md.user
|
|
1305
|
+
|
|
1306
|
+
@property
|
|
1307
|
+
def is_replica(self) -> bool:
|
|
1308
|
+
return self._tbl_md.is_replica
|
|
1309
|
+
|
|
1310
|
+
@property
|
|
1311
|
+
def comment(self) -> str:
|
|
1312
|
+
return self._schema_version_md.comment
|
|
1313
|
+
|
|
1314
|
+
@comment.setter
|
|
1315
|
+
def comment(self, c: str) -> None:
|
|
1316
|
+
assert self.effective_version is None
|
|
1317
|
+
self._schema_version_md.comment = c
|
|
1318
|
+
|
|
1319
|
+
@property
|
|
1320
|
+
def num_retained_versions(self) -> int:
|
|
1321
|
+
return self._schema_version_md.num_retained_versions
|
|
1246
1322
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
del self.external_stores[store_name]
|
|
1252
|
-
self._update_md(time.time(), update_tbl_version=False)
|
|
1323
|
+
@num_retained_versions.setter
|
|
1324
|
+
def num_retained_versions(self, n: int) -> None:
|
|
1325
|
+
assert self.effective_version is None
|
|
1326
|
+
self._schema_version_md.num_retained_versions = n
|
|
1253
1327
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1328
|
+
@property
|
|
1329
|
+
def version(self) -> int:
|
|
1330
|
+
# if this is a snapshot instance, we need to ignore current_version
|
|
1331
|
+
return self._tbl_md.current_version if self.effective_version is None else self.effective_version
|
|
1332
|
+
|
|
1333
|
+
@version.setter
|
|
1334
|
+
def version(self, version: int) -> None:
|
|
1335
|
+
assert self.effective_version is None
|
|
1336
|
+
self._tbl_md.current_version = version
|
|
1337
|
+
|
|
1338
|
+
@property
|
|
1339
|
+
def schema_version(self) -> int:
|
|
1340
|
+
return self._schema_version_md.schema_version
|
|
1341
|
+
|
|
1342
|
+
@schema_version.setter
|
|
1343
|
+
def schema_version(self, version: int) -> None:
|
|
1344
|
+
assert self.effective_version is None
|
|
1345
|
+
self._tbl_md.current_schema_version = version
|
|
1346
|
+
self._schema_version_md.schema_version = version
|
|
1347
|
+
|
|
1348
|
+
@property
|
|
1349
|
+
def preceding_schema_version(self) -> int:
|
|
1350
|
+
return self._schema_version_md.preceding_schema_version
|
|
1351
|
+
|
|
1352
|
+
@preceding_schema_version.setter
|
|
1353
|
+
def preceding_schema_version(self, v: int) -> None:
|
|
1354
|
+
assert self.effective_version is None
|
|
1355
|
+
self._schema_version_md.preceding_schema_version = v
|
|
1356
|
+
|
|
1357
|
+
@property
|
|
1358
|
+
def media_validation(self) -> MediaValidation:
|
|
1359
|
+
return MediaValidation[self._schema_version_md.media_validation.upper()]
|
|
1360
|
+
|
|
1361
|
+
@property
|
|
1362
|
+
def next_col_id(self) -> int:
|
|
1363
|
+
return self._tbl_md.next_col_id
|
|
1364
|
+
|
|
1365
|
+
@next_col_id.setter
|
|
1366
|
+
def next_col_id(self, id: int) -> None:
|
|
1367
|
+
assert self.effective_version is None
|
|
1368
|
+
self._tbl_md.next_col_id = id
|
|
1369
|
+
|
|
1370
|
+
@property
|
|
1371
|
+
def next_idx_id(self) -> int:
|
|
1372
|
+
return self._tbl_md.next_idx_id
|
|
1373
|
+
|
|
1374
|
+
@next_idx_id.setter
|
|
1375
|
+
def next_idx_id(self, id: int) -> None:
|
|
1376
|
+
assert self.effective_version is None
|
|
1377
|
+
self._tbl_md.next_idx_id = id
|
|
1378
|
+
|
|
1379
|
+
@property
|
|
1380
|
+
def next_row_id(self) -> int:
|
|
1381
|
+
return self._tbl_md.next_row_id
|
|
1382
|
+
|
|
1383
|
+
@next_row_id.setter
|
|
1384
|
+
def next_row_id(self, id: int) -> None:
|
|
1385
|
+
assert self.effective_version is None
|
|
1386
|
+
self._tbl_md.next_row_id = id
|
|
1256
1387
|
|
|
1257
1388
|
@property
|
|
1258
1389
|
def is_snapshot(self) -> bool:
|
|
1259
1390
|
return self.effective_version is not None
|
|
1260
1391
|
|
|
1392
|
+
@property
|
|
1393
|
+
def is_mutable(self) -> bool:
|
|
1394
|
+
return not self.is_snapshot and not self.is_replica
|
|
1395
|
+
|
|
1261
1396
|
@property
|
|
1262
1397
|
def is_view(self) -> bool:
|
|
1263
1398
|
return self.view_md is not None
|
|
@@ -1270,6 +1405,7 @@ class TableVersion:
|
|
|
1270
1405
|
def is_component_view(self) -> bool:
|
|
1271
1406
|
return self.iterator_cls is not None
|
|
1272
1407
|
|
|
1408
|
+
@property
|
|
1273
1409
|
def is_insertable(self) -> bool:
|
|
1274
1410
|
"""Returns True if this corresponds to an InsertableTable"""
|
|
1275
1411
|
return not self.is_snapshot and not self.is_view
|
|
@@ -1361,24 +1497,6 @@ class TableVersion:
|
|
|
1361
1497
|
{'class': f'{type(store).__module__}.{type(store).__qualname__}', 'md': store.as_dict()} for store in stores
|
|
1362
1498
|
]
|
|
1363
1499
|
|
|
1364
|
-
def _create_tbl_md(self) -> schema.TableMd:
|
|
1365
|
-
return schema.TableMd(
|
|
1366
|
-
tbl_id=str(self.id),
|
|
1367
|
-
name=self.name,
|
|
1368
|
-
user=self.user,
|
|
1369
|
-
is_replica=self.is_replica,
|
|
1370
|
-
current_version=self.version,
|
|
1371
|
-
current_schema_version=self.schema_version,
|
|
1372
|
-
next_col_id=self.next_col_id,
|
|
1373
|
-
next_idx_id=self.next_idx_id,
|
|
1374
|
-
next_row_id=self.next_rowid,
|
|
1375
|
-
column_md=self._create_column_md(self.cols),
|
|
1376
|
-
index_md=self.idx_md,
|
|
1377
|
-
external_stores=self._create_stores_md(self.external_stores.values()),
|
|
1378
|
-
view_md=self.view_md,
|
|
1379
|
-
additional_md={},
|
|
1380
|
-
)
|
|
1381
|
-
|
|
1382
1500
|
def _create_version_md(self, timestamp: float) -> schema.TableVersionMd:
|
|
1383
1501
|
return schema.TableVersionMd(
|
|
1384
1502
|
tbl_id=str(self.id),
|