pixeltable 0.3.15__py3-none-any.whl → 0.4.0rc2__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 +296 -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 +261 -189
- pixeltable/catalog/table_version.py +333 -202
- pixeltable/catalog/table_version_handle.py +15 -2
- pixeltable/catalog/table_version_path.py +60 -14
- pixeltable/catalog/view.py +38 -6
- pixeltable/dataframe.py +196 -18
- pixeltable/env.py +4 -4
- pixeltable/exec/__init__.py +1 -1
- pixeltable/exec/expr_eval/evaluators.py +4 -1
- pixeltable/exec/in_memory_data_node.py +1 -1
- pixeltable/exec/sql_node.py +171 -22
- pixeltable/exprs/column_property_ref.py +15 -6
- pixeltable/exprs/column_ref.py +32 -11
- pixeltable/exprs/comparison.py +1 -1
- pixeltable/exprs/data_row.py +5 -3
- pixeltable/exprs/expr.py +7 -0
- pixeltable/exprs/literal.py +2 -0
- 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/func/tools.py +1 -1
- pixeltable/functions/gemini.py +0 -1
- pixeltable/functions/string.py +212 -58
- pixeltable/globals.py +12 -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 +2 -2
- pixeltable/io/table_data_conduit.py +0 -31
- pixeltable/metadata/__init__.py +11 -2
- 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/convert_36.py +38 -0
- pixeltable/metadata/converters/util.py +3 -9
- pixeltable/metadata/notes.py +2 -0
- pixeltable/metadata/schema.py +8 -1
- pixeltable/plan.py +221 -14
- pixeltable/share/packager.py +137 -13
- pixeltable/share/publish.py +2 -2
- pixeltable/store.py +19 -13
- pixeltable/utils/dbms.py +1 -1
- pixeltable/utils/formatter.py +64 -42
- pixeltable/utils/sample.py +25 -0
- {pixeltable-0.3.15.dist-info → pixeltable-0.4.0rc2.dist-info}/METADATA +2 -1
- {pixeltable-0.3.15.dist-info → pixeltable-0.4.0rc2.dist-info}/RECORD +58 -55
- {pixeltable-0.3.15.dist-info → pixeltable-0.4.0rc2.dist-info}/LICENSE +0 -0
- {pixeltable-0.3.15.dist-info → pixeltable-0.4.0rc2.dist-info}/WHEEL +0 -0
- {pixeltable-0.3.15.dist-info → pixeltable-0.4.0rc2.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
|
|
@@ -22,6 +23,10 @@ from pixeltable.utils.exception_handler import run_cleanup_on_exception
|
|
|
22
23
|
from pixeltable.utils.filecache import FileCache
|
|
23
24
|
from pixeltable.utils.media_store import MediaStore
|
|
24
25
|
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from pixeltable.plan import SampleClause
|
|
28
|
+
|
|
29
|
+
|
|
25
30
|
from ..func.globals import resolve_symbol
|
|
26
31
|
from .column import Column
|
|
27
32
|
from .globals import _POS_COLUMN_NAME, _ROWID_COLUMN_NAME, MediaValidation, UpdateStatus, is_valid_identifier
|
|
@@ -50,43 +55,46 @@ class TableVersion:
|
|
|
50
55
|
|
|
51
56
|
Instances of TableVersion should not be stored as member variables (ie, used across transaction boundaries).
|
|
52
57
|
Use a TableVersionHandle instead.
|
|
58
|
+
|
|
59
|
+
Only TableVersion and Catalog interact directly with stored metadata. Everything else needs to go through these
|
|
60
|
+
two classes.
|
|
53
61
|
"""
|
|
54
62
|
|
|
55
63
|
id: UUID
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
|
|
65
|
+
# record metadata stored in catalog
|
|
66
|
+
_tbl_md: schema.TableMd
|
|
67
|
+
_schema_version_md: schema.TableSchemaVersionMd
|
|
68
|
+
|
|
58
69
|
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
70
|
path: Optional[pxt.catalog.TableVersionPath] # only set for live tables; needed to resolve computed cols
|
|
67
71
|
base: Optional[TableVersionHandle] # only set for views
|
|
68
|
-
next_col_id: int
|
|
69
|
-
next_idx_id: int
|
|
70
|
-
next_rowid: int
|
|
71
72
|
predicate: Optional[exprs.Expr]
|
|
72
|
-
|
|
73
|
+
sample_clause: Optional['SampleClause']
|
|
74
|
+
|
|
73
75
|
iterator_cls: Optional[type[ComponentIterator]]
|
|
74
76
|
iterator_args: Optional[exprs.InlineDict]
|
|
75
77
|
num_iterator_cols: int
|
|
76
78
|
|
|
79
|
+
# target for data operation propagation (only set for non-snapshots, and only records non-snapshot views)
|
|
80
|
+
mutable_views: set[TableVersionHandle]
|
|
81
|
+
|
|
77
82
|
# contains complete history of columns, incl dropped ones
|
|
78
83
|
cols: list[Column]
|
|
79
84
|
# contains only user-facing (named) columns visible in this version
|
|
80
85
|
cols_by_name: dict[str, Column]
|
|
81
86
|
# contains only columns visible in this version, both system and user
|
|
82
87
|
cols_by_id: dict[int, Column]
|
|
83
|
-
# needed for _create_tbl_md()
|
|
84
|
-
idx_md: dict[int, schema.IndexMd]
|
|
85
88
|
# contains only actively maintained indices
|
|
86
89
|
idxs_by_name: dict[str, TableVersion.IndexInfo]
|
|
87
90
|
|
|
88
91
|
external_stores: dict[str, pxt.io.ExternalStore]
|
|
89
|
-
store_tbl: 'store.StoreBase'
|
|
92
|
+
store_tbl: Optional['store.StoreBase']
|
|
93
|
+
|
|
94
|
+
# used by Catalog to invalidate cached instances at the end of a transaction;
|
|
95
|
+
# True if this instance reflects the state of stored metadata in the context of this transaction and
|
|
96
|
+
# it is the instance cached in Catalog
|
|
97
|
+
is_validated: bool
|
|
90
98
|
|
|
91
99
|
@dataclasses.dataclass
|
|
92
100
|
class IndexInfo:
|
|
@@ -106,21 +114,15 @@ class TableVersion:
|
|
|
106
114
|
mutable_views: list[TableVersionHandle],
|
|
107
115
|
base_path: Optional[pxt.catalog.TableVersionPath] = None,
|
|
108
116
|
base: Optional[TableVersionHandle] = None,
|
|
109
|
-
# base_store_tbl: Optional['store.StoreBase'] = None,
|
|
110
117
|
):
|
|
118
|
+
self.is_validated = True # a freshly constructed instance is always valid
|
|
111
119
|
self.id = id
|
|
112
|
-
self.
|
|
113
|
-
self.
|
|
120
|
+
self._tbl_md = copy.deepcopy(tbl_md)
|
|
121
|
+
self._schema_version_md = copy.deepcopy(schema_version_md)
|
|
114
122
|
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
123
|
assert not (self.is_view and base is None)
|
|
123
124
|
self.base = base
|
|
125
|
+
self.store_tbl = None
|
|
124
126
|
|
|
125
127
|
# mutable tables need their TableVersionPath for expr eval during updates
|
|
126
128
|
from .table_version_handle import TableVersionHandle
|
|
@@ -134,22 +136,14 @@ class TableVersion:
|
|
|
134
136
|
assert base_path is not None
|
|
135
137
|
self.path = TableVersionPath(self_handle, base=base_path)
|
|
136
138
|
|
|
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
139
|
# view-specific initialization
|
|
148
140
|
from pixeltable import exprs
|
|
141
|
+
from pixeltable.plan import SampleClause
|
|
149
142
|
|
|
150
143
|
predicate_dict = None if self.view_md is None or self.view_md.predicate is None else self.view_md.predicate
|
|
151
144
|
self.predicate = exprs.Expr.from_dict(predicate_dict) if predicate_dict is not None else None
|
|
152
|
-
self.
|
|
145
|
+
sample_dict = None if self.view_md is None or self.view_md.sample_clause is None else self.view_md.sample_clause
|
|
146
|
+
self.sample_clause = SampleClause.from_dict(sample_dict) if sample_dict is not None else None
|
|
153
147
|
|
|
154
148
|
# component view-specific initialization
|
|
155
149
|
self.iterator_cls = None
|
|
@@ -164,22 +158,26 @@ class TableVersion:
|
|
|
164
158
|
self.num_iterator_cols = len(output_schema)
|
|
165
159
|
assert tbl_md.view_md.iterator_args is not None
|
|
166
160
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
cat.add_tbl_version(self)
|
|
161
|
+
self.mutable_views = set(mutable_views)
|
|
162
|
+
assert self.is_mutable or len(self.mutable_views) == 0
|
|
170
163
|
|
|
171
|
-
# init schema after we determined whether we're a component view, and before we create the store table
|
|
172
164
|
self.cols = []
|
|
173
165
|
self.cols_by_name = {}
|
|
174
166
|
self.cols_by_id = {}
|
|
175
|
-
self.idx_md = tbl_md.index_md
|
|
176
167
|
self.idxs_by_name = {}
|
|
177
168
|
self.external_stores = {}
|
|
178
169
|
|
|
179
|
-
|
|
170
|
+
def init(self) -> None:
|
|
171
|
+
"""
|
|
172
|
+
Initialize schema-related in-memory metadata separately, now that this TableVersion instance is visible
|
|
173
|
+
in Catalog.
|
|
174
|
+
"""
|
|
175
|
+
from .catalog import Catalog
|
|
180
176
|
|
|
181
|
-
|
|
182
|
-
self.
|
|
177
|
+
assert (self.id, self.effective_version) in Catalog.get()._tbl_versions
|
|
178
|
+
self._init_schema()
|
|
179
|
+
# init external stores; this needs to happen after the schema is created
|
|
180
|
+
self._init_external_stores()
|
|
183
181
|
|
|
184
182
|
def __hash__(self) -> int:
|
|
185
183
|
return hash(self.id)
|
|
@@ -188,19 +186,7 @@ class TableVersion:
|
|
|
188
186
|
"""Create a snapshot copy of this TableVersion"""
|
|
189
187
|
assert not self.is_snapshot
|
|
190
188
|
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)
|
|
189
|
+
return TableVersion(self.id, self.tbl_md, self.version, self.schema_version_md, mutable_views=[], base=base)
|
|
204
190
|
|
|
205
191
|
@property
|
|
206
192
|
def versioned_name(self) -> str:
|
|
@@ -292,7 +278,13 @@ class TableVersion:
|
|
|
292
278
|
|
|
293
279
|
# if this is purely a snapshot (it doesn't require any additional storage for columns and it doesn't have a
|
|
294
280
|
# predicate to apply at runtime), we don't create a physical table and simply use the base's table version path
|
|
295
|
-
if
|
|
281
|
+
if (
|
|
282
|
+
view_md is not None
|
|
283
|
+
and view_md.is_snapshot
|
|
284
|
+
and view_md.predicate is None
|
|
285
|
+
and view_md.sample_clause is None
|
|
286
|
+
and len(cols) == 0
|
|
287
|
+
):
|
|
296
288
|
session.add(tbl_record)
|
|
297
289
|
session.add(tbl_version_record)
|
|
298
290
|
session.add(schema_version_record)
|
|
@@ -306,8 +298,19 @@ class TableVersion:
|
|
|
306
298
|
tbl_version = cls(
|
|
307
299
|
tbl_record.id, table_md, effective_version, schema_version_md, [], base_path=base_path, base=base
|
|
308
300
|
)
|
|
309
|
-
|
|
301
|
+
# TODO: break this up, so that Catalog.create_table() registers tbl_version
|
|
302
|
+
cat = pxt.catalog.Catalog.get()
|
|
303
|
+
cat._tbl_versions[tbl_record.id, effective_version] = tbl_version
|
|
304
|
+
tbl_version.init()
|
|
310
305
|
tbl_version.store_tbl.create()
|
|
306
|
+
is_mutable = not is_snapshot and not table_md.is_replica
|
|
307
|
+
if base is not None and base.get().is_mutable and is_mutable:
|
|
308
|
+
from .table_version_handle import TableVersionHandle
|
|
309
|
+
|
|
310
|
+
handle = TableVersionHandle(tbl_version.id, effective_version)
|
|
311
|
+
assert handle not in base.get().mutable_views
|
|
312
|
+
base.get().mutable_views.add(handle)
|
|
313
|
+
|
|
311
314
|
if view_md is None or not view_md.is_snapshot:
|
|
312
315
|
# add default indices, after creating the store table
|
|
313
316
|
for col in tbl_version.cols_by_name.values():
|
|
@@ -315,7 +318,7 @@ class TableVersion:
|
|
|
315
318
|
assert status is None or status.num_excs == 0
|
|
316
319
|
|
|
317
320
|
# 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.
|
|
321
|
+
tbl_record = schema.Table(id=tbl_id, dir_id=dir_id, md=dataclasses.asdict(tbl_version.tbl_md))
|
|
319
322
|
session.add(tbl_record)
|
|
320
323
|
session.add(tbl_version_record)
|
|
321
324
|
session.add(schema_version_record)
|
|
@@ -331,6 +334,9 @@ class TableVersion:
|
|
|
331
334
|
tbl_version = cls(
|
|
332
335
|
tbl_id, md.tbl_md, md.version_md.version, md.schema_version_md, [], base_path=base_path, base=base
|
|
333
336
|
)
|
|
337
|
+
cat = pxt.catalog.Catalog.get()
|
|
338
|
+
cat._tbl_versions[tbl_version.id, tbl_version.effective_version] = tbl_version
|
|
339
|
+
tbl_version.init()
|
|
334
340
|
tbl_version.store_tbl.create()
|
|
335
341
|
tbl_version.store_tbl.ensure_columns_exist(col for col in tbl_version.cols if col.is_stored)
|
|
336
342
|
return tbl_version
|
|
@@ -338,6 +344,14 @@ class TableVersion:
|
|
|
338
344
|
def drop(self) -> None:
|
|
339
345
|
from .catalog import Catalog
|
|
340
346
|
|
|
347
|
+
if self.is_view and self.is_mutable:
|
|
348
|
+
# update mutable_views
|
|
349
|
+
from .table_version_handle import TableVersionHandle
|
|
350
|
+
|
|
351
|
+
assert self.base is not None
|
|
352
|
+
if self.base.get().is_mutable:
|
|
353
|
+
self.base.get().mutable_views.remove(TableVersionHandle.create(self))
|
|
354
|
+
|
|
341
355
|
cat = Catalog.get()
|
|
342
356
|
# delete this table and all associated data
|
|
343
357
|
MediaStore.delete(self.id)
|
|
@@ -347,24 +361,24 @@ class TableVersion:
|
|
|
347
361
|
# de-register table version from catalog
|
|
348
362
|
cat.remove_tbl_version(self)
|
|
349
363
|
|
|
350
|
-
def _init_schema(self
|
|
364
|
+
def _init_schema(self) -> None:
|
|
351
365
|
# create columns first, so the indices can reference them
|
|
352
|
-
self._init_cols(
|
|
366
|
+
self._init_cols()
|
|
353
367
|
if not self.is_snapshot:
|
|
354
|
-
self._init_idxs(
|
|
368
|
+
self._init_idxs()
|
|
355
369
|
# create the sa schema only after creating the columns and indices
|
|
356
370
|
self._init_sa_schema()
|
|
357
371
|
|
|
358
|
-
def _init_cols(self
|
|
372
|
+
def _init_cols(self) -> None:
|
|
359
373
|
"""Initialize self.cols with the columns visible in our effective version"""
|
|
360
374
|
self.cols = []
|
|
361
375
|
self.cols_by_name = {}
|
|
362
376
|
self.cols_by_id = {}
|
|
363
377
|
# Sort columns in column_md by the position specified in col_md.id to guarantee that all references
|
|
364
378
|
# point backward.
|
|
365
|
-
sorted_column_md = sorted(tbl_md.column_md.values(), key=lambda item: item.id)
|
|
379
|
+
sorted_column_md = sorted(self.tbl_md.column_md.values(), key=lambda item: item.id)
|
|
366
380
|
for col_md in sorted_column_md:
|
|
367
|
-
schema_col_md = schema_version_md.columns.get(col_md.id)
|
|
381
|
+
schema_col_md = self.schema_version_md.columns.get(col_md.id)
|
|
368
382
|
col_name = schema_col_md.name if schema_col_md is not None else None
|
|
369
383
|
media_val = (
|
|
370
384
|
MediaValidation[schema_col_md.media_validation.upper()]
|
|
@@ -382,7 +396,7 @@ class TableVersion:
|
|
|
382
396
|
schema_version_drop=col_md.schema_version_drop,
|
|
383
397
|
value_expr_dict=col_md.value_expr,
|
|
384
398
|
)
|
|
385
|
-
col.tbl = self
|
|
399
|
+
col.tbl = self
|
|
386
400
|
self.cols.append(col)
|
|
387
401
|
|
|
388
402
|
# populate the lookup structures before Expr.from_dict()
|
|
@@ -401,12 +415,12 @@ class TableVersion:
|
|
|
401
415
|
if not self.is_snapshot and col_md.value_expr is not None:
|
|
402
416
|
self._record_refd_columns(col)
|
|
403
417
|
|
|
404
|
-
def _init_idxs(self
|
|
405
|
-
self.idx_md = tbl_md.index_md
|
|
418
|
+
def _init_idxs(self) -> None:
|
|
419
|
+
# self.idx_md = tbl_md.index_md
|
|
406
420
|
self.idxs_by_name = {}
|
|
407
421
|
import pixeltable.index as index_module
|
|
408
422
|
|
|
409
|
-
for md in tbl_md.index_md.values():
|
|
423
|
+
for md in self.tbl_md.index_md.values():
|
|
410
424
|
if md.schema_version_add > self.schema_version or (
|
|
411
425
|
md.schema_version_drop is not None and md.schema_version_drop <= self.schema_version
|
|
412
426
|
):
|
|
@@ -441,28 +455,32 @@ class TableVersion:
|
|
|
441
455
|
else:
|
|
442
456
|
self.store_tbl = StoreTable(self)
|
|
443
457
|
|
|
444
|
-
def
|
|
445
|
-
self, timestamp: float, update_tbl_version: bool = True, preceding_schema_version: Optional[int] = None
|
|
446
|
-
) -> None:
|
|
458
|
+
def _write_md(self, new_version: bool, new_version_ts: float, new_schema_version: bool) -> None:
|
|
447
459
|
"""Writes table metadata to the database.
|
|
448
460
|
|
|
449
461
|
Args:
|
|
450
462
|
timestamp: timestamp of the change
|
|
451
|
-
conn: database connection to use
|
|
452
463
|
update_tbl_version: if `True`, will also write `TableVersion` metadata
|
|
453
464
|
preceding_schema_version: if specified, will also write `TableSchemaVersion` metadata, recording the
|
|
454
465
|
specified preceding schema version
|
|
455
466
|
"""
|
|
456
|
-
assert update_tbl_version or preceding_schema_version is None
|
|
457
467
|
from pixeltable.catalog import Catalog
|
|
458
468
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
469
|
+
version_md: Optional[schema.TableVersionMd] = (
|
|
470
|
+
schema.TableVersionMd(
|
|
471
|
+
tbl_id=str(self.id),
|
|
472
|
+
created_at=new_version_ts,
|
|
473
|
+
version=self.version,
|
|
474
|
+
schema_version=self.schema_version,
|
|
475
|
+
additional_md={},
|
|
476
|
+
)
|
|
477
|
+
if new_version
|
|
478
|
+
else None
|
|
463
479
|
)
|
|
464
480
|
|
|
465
|
-
Catalog.get().store_tbl_md(
|
|
481
|
+
Catalog.get().store_tbl_md(
|
|
482
|
+
self.id, self._tbl_md, version_md, self._schema_version_md if new_schema_version else None
|
|
483
|
+
)
|
|
466
484
|
|
|
467
485
|
def ensure_md_loaded(self) -> None:
|
|
468
486
|
"""Ensure that table metadata is loaded."""
|
|
@@ -476,10 +494,10 @@ class TableVersion:
|
|
|
476
494
|
def add_index(self, col: Column, idx_name: Optional[str], idx: index.IndexBase) -> UpdateStatus:
|
|
477
495
|
# we're creating a new schema version
|
|
478
496
|
self.version += 1
|
|
479
|
-
preceding_schema_version = self.schema_version
|
|
497
|
+
self.preceding_schema_version = self.schema_version
|
|
480
498
|
self.schema_version = self.version
|
|
481
499
|
status = self._add_index(col, idx_name, idx)
|
|
482
|
-
self.
|
|
500
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
483
501
|
_logger.info(f'Added index {idx_name} on column {col.name} to table {self.name}')
|
|
484
502
|
return status
|
|
485
503
|
|
|
@@ -524,7 +542,7 @@ class TableVersion:
|
|
|
524
542
|
schema_version_drop=None,
|
|
525
543
|
records_errors=idx.records_value_errors(),
|
|
526
544
|
)
|
|
527
|
-
val_col.tbl = self
|
|
545
|
+
val_col.tbl = self
|
|
528
546
|
val_col.col_type = val_col.col_type.copy(nullable=True)
|
|
529
547
|
self.next_col_id += 1
|
|
530
548
|
|
|
@@ -538,7 +556,7 @@ class TableVersion:
|
|
|
538
556
|
schema_version_drop=None,
|
|
539
557
|
records_errors=False,
|
|
540
558
|
)
|
|
541
|
-
undo_col.tbl = self
|
|
559
|
+
undo_col.tbl = self
|
|
542
560
|
undo_col.col_type = undo_col.col_type.copy(nullable=True)
|
|
543
561
|
self.next_col_id += 1
|
|
544
562
|
return val_col, undo_col
|
|
@@ -553,7 +571,7 @@ class TableVersion:
|
|
|
553
571
|
idx_name = f'idx{idx_id}'
|
|
554
572
|
else:
|
|
555
573
|
assert is_valid_identifier(idx_name)
|
|
556
|
-
assert idx_name not in [i.name for i in self.
|
|
574
|
+
assert idx_name not in [i.name for i in self._tbl_md.index_md.values()]
|
|
557
575
|
# create and register the index metadata
|
|
558
576
|
idx_cls = type(idx)
|
|
559
577
|
idx_md = schema.IndexMd(
|
|
@@ -569,7 +587,7 @@ class TableVersion:
|
|
|
569
587
|
init_args=idx.as_dict(),
|
|
570
588
|
)
|
|
571
589
|
idx_info = self.IndexInfo(id=idx_id, name=idx_name, idx=idx, col=col, val_col=val_col, undo_col=undo_col)
|
|
572
|
-
self.
|
|
590
|
+
self._tbl_md.index_md[idx_id] = idx_md
|
|
573
591
|
self.idxs_by_name[idx_name] = idx_info
|
|
574
592
|
try:
|
|
575
593
|
idx.create_index(self._store_idx_name(idx_id), val_col)
|
|
@@ -578,7 +596,7 @@ class TableVersion:
|
|
|
578
596
|
def cleanup_index() -> None:
|
|
579
597
|
"""Delete the newly added in-memory index structure"""
|
|
580
598
|
del self.idxs_by_name[idx_name]
|
|
581
|
-
del self.
|
|
599
|
+
del self._tbl_md.index_md[idx_id]
|
|
582
600
|
self.next_idx_id = idx_id
|
|
583
601
|
|
|
584
602
|
# Run cleanup only if there has been an exception; otherwise, skip cleanup.
|
|
@@ -596,47 +614,48 @@ class TableVersion:
|
|
|
596
614
|
|
|
597
615
|
def drop_index(self, idx_id: int) -> None:
|
|
598
616
|
assert not self.is_snapshot
|
|
599
|
-
assert idx_id in self.
|
|
617
|
+
assert idx_id in self._tbl_md.index_md
|
|
600
618
|
|
|
601
619
|
# we're creating a new schema version
|
|
602
620
|
self.version += 1
|
|
603
|
-
preceding_schema_version = self.schema_version
|
|
621
|
+
self.preceding_schema_version = self.schema_version
|
|
604
622
|
self.schema_version = self.version
|
|
605
|
-
idx_md = self.
|
|
623
|
+
idx_md = self._tbl_md.index_md[idx_id]
|
|
606
624
|
idx_md.schema_version_drop = self.schema_version
|
|
607
625
|
assert idx_md.name in self.idxs_by_name
|
|
608
626
|
idx_info = self.idxs_by_name[idx_md.name]
|
|
609
627
|
# remove this index entry from the active indexes (in memory)
|
|
610
628
|
# and the index metadata (in persistent table metadata)
|
|
629
|
+
# TODO: this is wrong, it breaks revert()
|
|
611
630
|
del self.idxs_by_name[idx_md.name]
|
|
612
|
-
del self.
|
|
631
|
+
del self._tbl_md.index_md[idx_id]
|
|
613
632
|
|
|
614
633
|
self._drop_columns([idx_info.val_col, idx_info.undo_col])
|
|
615
|
-
self.
|
|
634
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
616
635
|
_logger.info(f'Dropped index {idx_md.name} on table {self.name}')
|
|
617
636
|
|
|
618
637
|
def add_columns(
|
|
619
638
|
self, cols: Iterable[Column], print_stats: bool, on_error: Literal['abort', 'ignore']
|
|
620
639
|
) -> UpdateStatus:
|
|
621
|
-
"""Adds
|
|
640
|
+
"""Adds columns to the table."""
|
|
622
641
|
assert not self.is_snapshot
|
|
623
|
-
assert all(is_valid_identifier(col.name) for col in cols)
|
|
642
|
+
assert all(is_valid_identifier(col.name) for col in cols if col.name is not None)
|
|
624
643
|
assert all(col.stored is not None for col in cols)
|
|
625
|
-
assert all(col.name not in self.cols_by_name for col in cols)
|
|
644
|
+
assert all(col.name not in self.cols_by_name for col in cols if col.name is not None)
|
|
626
645
|
for col in cols:
|
|
627
|
-
col.tbl = self
|
|
646
|
+
col.tbl = self
|
|
628
647
|
col.id = self.next_col_id
|
|
629
648
|
self.next_col_id += 1
|
|
630
649
|
|
|
631
650
|
# we're creating a new schema version
|
|
632
651
|
self.version += 1
|
|
633
|
-
preceding_schema_version = self.schema_version
|
|
652
|
+
self.preceding_schema_version = self.schema_version
|
|
634
653
|
self.schema_version = self.version
|
|
635
654
|
index_cols: dict[Column, tuple[index.BtreeIndex, Column, Column]] = {}
|
|
636
655
|
all_cols: list[Column] = []
|
|
637
656
|
for col in cols:
|
|
638
657
|
all_cols.append(col)
|
|
639
|
-
if self._is_btree_indexable(col):
|
|
658
|
+
if col.name is not None and self._is_btree_indexable(col):
|
|
640
659
|
idx = index.BtreeIndex(col)
|
|
641
660
|
val_col, undo_col = self._create_index_columns(idx)
|
|
642
661
|
index_cols[col] = (idx, val_col, undo_col)
|
|
@@ -644,10 +663,10 @@ class TableVersion:
|
|
|
644
663
|
all_cols.append(undo_col)
|
|
645
664
|
# Add all columns
|
|
646
665
|
status = self._add_columns(all_cols, print_stats=print_stats, on_error=on_error)
|
|
647
|
-
# Create indices and their
|
|
666
|
+
# Create indices and their md records
|
|
648
667
|
for col, (idx, val_col, undo_col) in index_cols.items():
|
|
649
668
|
self._create_index(col, val_col, undo_col, idx_name=None, idx=idx)
|
|
650
|
-
self.
|
|
669
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
651
670
|
_logger.info(f'Added columns {[col.name for col in cols]} to table {self.name}, new version: {self.version}')
|
|
652
671
|
|
|
653
672
|
msg = (
|
|
@@ -685,6 +704,23 @@ class TableVersion:
|
|
|
685
704
|
col.check_value_expr()
|
|
686
705
|
self._record_refd_columns(col)
|
|
687
706
|
|
|
707
|
+
# also add to stored md
|
|
708
|
+
self._tbl_md.column_md[col.id] = schema.ColumnMd(
|
|
709
|
+
id=col.id,
|
|
710
|
+
col_type=col.col_type.as_dict(),
|
|
711
|
+
is_pk=col.is_pk,
|
|
712
|
+
schema_version_add=col.schema_version_add,
|
|
713
|
+
schema_version_drop=col.schema_version_drop,
|
|
714
|
+
value_expr=col.value_expr.as_dict() if col.value_expr is not None else None,
|
|
715
|
+
stored=col.stored,
|
|
716
|
+
)
|
|
717
|
+
if col.name is not None:
|
|
718
|
+
self._schema_version_md.columns[col.id] = schema.SchemaColumn(
|
|
719
|
+
name=col.name,
|
|
720
|
+
pos=len(self.cols_by_name),
|
|
721
|
+
media_validation=col._media_validation.name.lower() if col._media_validation is not None else None,
|
|
722
|
+
)
|
|
723
|
+
|
|
688
724
|
if col.is_stored:
|
|
689
725
|
self.store_tbl.add_column(col)
|
|
690
726
|
|
|
@@ -731,7 +767,7 @@ class TableVersion:
|
|
|
731
767
|
num_rows=row_count,
|
|
732
768
|
num_computed_values=row_count,
|
|
733
769
|
num_excs=num_excs,
|
|
734
|
-
cols_with_excs=[f'{col.tbl.
|
|
770
|
+
cols_with_excs=[f'{col.tbl.name}.{col.name}' for col in cols_with_excs if col.name is not None],
|
|
735
771
|
)
|
|
736
772
|
|
|
737
773
|
def drop_column(self, col: Column) -> None:
|
|
@@ -741,7 +777,7 @@ class TableVersion:
|
|
|
741
777
|
|
|
742
778
|
# we're creating a new schema version
|
|
743
779
|
self.version += 1
|
|
744
|
-
preceding_schema_version = self.schema_version
|
|
780
|
+
self.preceding_schema_version = self.schema_version
|
|
745
781
|
self.schema_version = self.version
|
|
746
782
|
|
|
747
783
|
# drop this column and all dependent index columns and indices
|
|
@@ -751,15 +787,17 @@ class TableVersion:
|
|
|
751
787
|
if idx_info.col != col:
|
|
752
788
|
continue
|
|
753
789
|
dropped_cols.extend([idx_info.val_col, idx_info.undo_col])
|
|
754
|
-
idx_md = self.
|
|
790
|
+
idx_md = self._tbl_md.index_md[idx_info.id]
|
|
755
791
|
idx_md.schema_version_drop = self.schema_version
|
|
756
792
|
assert idx_md.name in self.idxs_by_name
|
|
757
793
|
dropped_idx_names.append(idx_md.name)
|
|
794
|
+
|
|
758
795
|
# update idxs_by_name
|
|
759
796
|
for idx_name in dropped_idx_names:
|
|
760
797
|
del self.idxs_by_name[idx_name]
|
|
798
|
+
|
|
761
799
|
self._drop_columns(dropped_cols)
|
|
762
|
-
self.
|
|
800
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
763
801
|
_logger.info(f'Dropped column {col.name} from table {self.name}, new version: {self.version}')
|
|
764
802
|
|
|
765
803
|
def _drop_columns(self, cols: Iterable[Column]) -> None:
|
|
@@ -780,6 +818,14 @@ class TableVersion:
|
|
|
780
818
|
del self.cols_by_name[col.name]
|
|
781
819
|
assert col.id in self.cols_by_id
|
|
782
820
|
del self.cols_by_id[col.id]
|
|
821
|
+
# update stored md
|
|
822
|
+
self._tbl_md.column_md[col.id].schema_version_drop = col.schema_version_drop
|
|
823
|
+
if col.name is not None:
|
|
824
|
+
del self._schema_version_md.columns[col.id]
|
|
825
|
+
|
|
826
|
+
# update positions
|
|
827
|
+
for pos, schema_col in enumerate(self._schema_version_md.columns.values()):
|
|
828
|
+
schema_col.pos = pos
|
|
783
829
|
|
|
784
830
|
self.store_tbl.create_sa_tbl()
|
|
785
831
|
|
|
@@ -796,13 +842,14 @@ class TableVersion:
|
|
|
796
842
|
del self.cols_by_name[old_name]
|
|
797
843
|
col.name = new_name
|
|
798
844
|
self.cols_by_name[new_name] = col
|
|
845
|
+
self._schema_version_md.columns[col.id].name = new_name
|
|
799
846
|
|
|
800
847
|
# we're creating a new schema version
|
|
801
848
|
self.version += 1
|
|
802
|
-
preceding_schema_version = self.schema_version
|
|
849
|
+
self.preceding_schema_version = self.schema_version
|
|
803
850
|
self.schema_version = self.version
|
|
804
851
|
|
|
805
|
-
self.
|
|
852
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
806
853
|
_logger.info(f'Renamed column {old_name} to {new_name} in table {self.name}, new version: {self.version}')
|
|
807
854
|
|
|
808
855
|
def set_comment(self, new_comment: Optional[str]) -> None:
|
|
@@ -821,9 +868,9 @@ class TableVersion:
|
|
|
821
868
|
def _create_schema_version(self) -> None:
|
|
822
869
|
# we're creating a new schema version
|
|
823
870
|
self.version += 1
|
|
824
|
-
preceding_schema_version = self.schema_version
|
|
871
|
+
self.preceding_schema_version = self.schema_version
|
|
825
872
|
self.schema_version = self.version
|
|
826
|
-
self.
|
|
873
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
827
874
|
_logger.info(f'[{self.name}] Updating table schema to version: {self.version}')
|
|
828
875
|
|
|
829
876
|
def insert(
|
|
@@ -838,7 +885,7 @@ class TableVersion:
|
|
|
838
885
|
"""
|
|
839
886
|
from pixeltable.plan import Planner
|
|
840
887
|
|
|
841
|
-
assert self.is_insertable
|
|
888
|
+
assert self.is_insertable
|
|
842
889
|
assert (rows is None) != (df is None) # Exactly one must be specified
|
|
843
890
|
if rows is not None:
|
|
844
891
|
plan = Planner.create_insert_plan(self, rows, ignore_errors=not fail_on_exception)
|
|
@@ -848,8 +895,8 @@ class TableVersion:
|
|
|
848
895
|
# this is a base table; we generate rowids during the insert
|
|
849
896
|
def rowids() -> Iterator[int]:
|
|
850
897
|
while True:
|
|
851
|
-
rowid = self.
|
|
852
|
-
self.
|
|
898
|
+
rowid = self.next_row_id
|
|
899
|
+
self.next_row_id += 1
|
|
853
900
|
yield rowid
|
|
854
901
|
|
|
855
902
|
return self._insert(plan, time.time(), print_stats=print_stats, rowids=rowids(), abort_on_exc=fail_on_exception)
|
|
@@ -874,7 +921,7 @@ class TableVersion:
|
|
|
874
921
|
result.num_excs = num_excs
|
|
875
922
|
result.num_computed_values += exec_plan.ctx.num_computed_exprs * num_rows
|
|
876
923
|
result.cols_with_excs = [f'{self.name}.{self.cols_by_id[cid].name}' for cid in cols_with_excs]
|
|
877
|
-
self.
|
|
924
|
+
self._write_md(new_version=True, new_version_ts=timestamp, new_schema_version=False)
|
|
878
925
|
|
|
879
926
|
# update views
|
|
880
927
|
for view in self.mutable_views:
|
|
@@ -1038,13 +1085,13 @@ class TableVersion:
|
|
|
1038
1085
|
self.store_tbl.delete_rows(
|
|
1039
1086
|
self.version, base_versions=base_versions, match_on_vmin=True, where_clause=where_clause
|
|
1040
1087
|
)
|
|
1041
|
-
self.
|
|
1088
|
+
self._write_md(new_version=True, new_version_ts=timestamp, new_schema_version=False)
|
|
1042
1089
|
|
|
1043
1090
|
if cascade:
|
|
1044
1091
|
base_versions = [None if plan is None else self.version, *base_versions] # don't update in place
|
|
1045
1092
|
# propagate to views
|
|
1046
1093
|
for view in self.mutable_views:
|
|
1047
|
-
recomputed_cols = [col for col in recomputed_view_cols if col.tbl == view]
|
|
1094
|
+
recomputed_cols = [col for col in recomputed_view_cols if col.tbl.id == view.id]
|
|
1048
1095
|
plan = None
|
|
1049
1096
|
if len(recomputed_cols) > 0:
|
|
1050
1097
|
from pixeltable.plan import Planner
|
|
@@ -1065,7 +1112,7 @@ class TableVersion:
|
|
|
1065
1112
|
Args:
|
|
1066
1113
|
where: a predicate to filter rows to delete.
|
|
1067
1114
|
"""
|
|
1068
|
-
assert self.is_insertable
|
|
1115
|
+
assert self.is_insertable
|
|
1069
1116
|
from pixeltable.exprs import Expr
|
|
1070
1117
|
from pixeltable.plan import Planner
|
|
1071
1118
|
|
|
@@ -1093,14 +1140,22 @@ class TableVersion:
|
|
|
1093
1140
|
Returns:
|
|
1094
1141
|
number of deleted rows
|
|
1095
1142
|
"""
|
|
1143
|
+
# print(f'calling sql_expr()')
|
|
1096
1144
|
sql_where_clause = where.sql_expr(exprs.SqlElementCache()) if where is not None else None
|
|
1145
|
+
# #print(f'sql_where_clause={str(sql_where_clause) if sql_where_clause is not None else None}')
|
|
1146
|
+
# sql_cols: list[sql.Column] = []
|
|
1147
|
+
# def collect_cols(col) -> None:
|
|
1148
|
+
# sql_cols.append(col)
|
|
1149
|
+
# sql.sql.visitors.traverse(sql_where_clause, {}, {'column': collect_cols})
|
|
1150
|
+
# x = [f'{str(c)}:{hash(c)}:{id(c.table)}' for c in sql_cols]
|
|
1151
|
+
# print(f'where_clause cols: {x}')
|
|
1097
1152
|
num_rows = self.store_tbl.delete_rows(
|
|
1098
1153
|
self.version + 1, base_versions=base_versions, match_on_vmin=False, where_clause=sql_where_clause
|
|
1099
1154
|
)
|
|
1100
1155
|
if num_rows > 0:
|
|
1101
1156
|
# we're creating a new version
|
|
1102
1157
|
self.version += 1
|
|
1103
|
-
self.
|
|
1158
|
+
self._write_md(new_version=True, new_version_ts=timestamp, new_schema_version=False)
|
|
1104
1159
|
for view in self.mutable_views:
|
|
1105
1160
|
num_rows += view.get().propagate_delete(
|
|
1106
1161
|
where=None, base_versions=[self.version, *base_versions], timestamp=timestamp
|
|
@@ -1114,17 +1169,13 @@ class TableVersion:
|
|
|
1114
1169
|
raise excs.Error('Cannot revert version 0')
|
|
1115
1170
|
self._revert()
|
|
1116
1171
|
|
|
1117
|
-
def _delete_column(self, col: Column) -> None:
|
|
1118
|
-
"""Physically remove the column from the schema and the store table"""
|
|
1119
|
-
if col.is_stored:
|
|
1120
|
-
self.store_tbl.drop_column(col)
|
|
1121
|
-
self.cols.remove(col)
|
|
1122
|
-
if col.name is not None:
|
|
1123
|
-
del self.cols_by_name[col.name]
|
|
1124
|
-
del self.cols_by_id[col.id]
|
|
1125
|
-
|
|
1126
1172
|
def _revert(self) -> None:
|
|
1127
|
-
"""
|
|
1173
|
+
"""
|
|
1174
|
+
Reverts the stored metadata for this table version and propagates to views.
|
|
1175
|
+
|
|
1176
|
+
Doesn't attempt to revert the in-memory metadata, but instead invalidates this TableVersion instance
|
|
1177
|
+
and relies on Catalog to reload it
|
|
1178
|
+
"""
|
|
1128
1179
|
conn = Env.get().conn
|
|
1129
1180
|
# make sure we don't have a snapshot referencing this version
|
|
1130
1181
|
# (unclear how to express this with sqlalchemy)
|
|
@@ -1157,109 +1208,206 @@ class TableVersion:
|
|
|
1157
1208
|
stmt = sql.update(self.store_tbl.sa_tbl).values(set_clause).where(self.store_tbl.sa_tbl.c.v_max == self.version)
|
|
1158
1209
|
conn.execute(stmt)
|
|
1159
1210
|
|
|
1160
|
-
# revert schema changes
|
|
1211
|
+
# revert schema changes:
|
|
1212
|
+
# - undo changes to self._tbl_md and write that back
|
|
1213
|
+
# - delete newly-added TableVersion/TableSchemaVersion records
|
|
1161
1214
|
if self.version == self.schema_version:
|
|
1162
|
-
# delete newly-added columns
|
|
1215
|
+
# physically delete newly-added columns and remove them from the stored md
|
|
1163
1216
|
added_cols = [col for col in self.cols if col.schema_version_add == self.schema_version]
|
|
1164
1217
|
if len(added_cols) > 0:
|
|
1165
|
-
next_col_id = min(col.id for col in added_cols)
|
|
1218
|
+
self._tbl_md.next_col_id = min(col.id for col in added_cols)
|
|
1166
1219
|
for col in added_cols:
|
|
1167
|
-
|
|
1168
|
-
|
|
1220
|
+
if col.is_stored:
|
|
1221
|
+
self.store_tbl.drop_column(col)
|
|
1222
|
+
del self._tbl_md.column_md[col.id]
|
|
1169
1223
|
|
|
1170
1224
|
# remove newly-added indices from the lookup structures
|
|
1171
1225
|
# (the value and undo columns got removed in the preceding step)
|
|
1172
|
-
added_idx_md = [md for md in self.
|
|
1226
|
+
added_idx_md = [md for md in self._tbl_md.index_md.values() if md.schema_version_add == self.schema_version]
|
|
1173
1227
|
if len(added_idx_md) > 0:
|
|
1174
|
-
next_idx_id = min(md.id for md in added_idx_md)
|
|
1228
|
+
self._tbl_md.next_idx_id = min(md.id for md in added_idx_md)
|
|
1175
1229
|
for md in added_idx_md:
|
|
1176
|
-
|
|
1177
|
-
del self.
|
|
1178
|
-
self.next_idx_id = next_idx_id
|
|
1230
|
+
# TODO: drop the index
|
|
1231
|
+
del self._tbl_md.index_md[md.id]
|
|
1179
1232
|
|
|
1180
1233
|
# make newly-dropped columns visible again
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1234
|
+
dropped_col_md = [
|
|
1235
|
+
md for md in self._tbl_md.column_md.values() if md.schema_version_drop == self.schema_version
|
|
1236
|
+
]
|
|
1237
|
+
for col_md in dropped_col_md:
|
|
1238
|
+
col_md.schema_version_drop = None
|
|
1184
1239
|
|
|
1185
1240
|
# make newly-dropped indices visible again
|
|
1186
|
-
dropped_idx_md = [
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
# we need to determine the preceding schema version and reload the schema
|
|
1192
|
-
schema_version_md_dict = (
|
|
1193
|
-
session.query(schema.TableSchemaVersion.md)
|
|
1194
|
-
.where(schema.TableSchemaVersion.tbl_id == self.id)
|
|
1195
|
-
.where(schema.TableSchemaVersion.schema_version == self.schema_version)
|
|
1196
|
-
.scalar()
|
|
1197
|
-
)
|
|
1198
|
-
preceding_schema_version = schema_version_md_dict['preceding_schema_version']
|
|
1199
|
-
preceding_schema_version_md_dict = (
|
|
1200
|
-
session.query(schema.TableSchemaVersion.md)
|
|
1201
|
-
.where(schema.TableSchemaVersion.tbl_id == self.id)
|
|
1202
|
-
.where(schema.TableSchemaVersion.schema_version == preceding_schema_version)
|
|
1203
|
-
.scalar()
|
|
1204
|
-
)
|
|
1205
|
-
preceding_schema_version_md = schema.md_from_dict(
|
|
1206
|
-
schema.TableSchemaVersionMd, preceding_schema_version_md_dict
|
|
1207
|
-
)
|
|
1208
|
-
tbl_md = self._create_tbl_md()
|
|
1209
|
-
self._init_schema(tbl_md, preceding_schema_version_md)
|
|
1241
|
+
dropped_idx_md = [
|
|
1242
|
+
md for md in self._tbl_md.index_md.values() if md.schema_version_drop == self.schema_version
|
|
1243
|
+
]
|
|
1244
|
+
for idx_md in dropped_idx_md:
|
|
1245
|
+
idx_md.schema_version_drop = None
|
|
1210
1246
|
|
|
1211
1247
|
conn.execute(
|
|
1212
1248
|
sql.delete(schema.TableSchemaVersion.__table__)
|
|
1213
1249
|
.where(schema.TableSchemaVersion.tbl_id == self.id)
|
|
1214
1250
|
.where(schema.TableSchemaVersion.schema_version == self.schema_version)
|
|
1215
1251
|
)
|
|
1216
|
-
self.
|
|
1217
|
-
self.comment = preceding_schema_version_md.comment
|
|
1218
|
-
self.num_retained_versions = preceding_schema_version_md.num_retained_versions
|
|
1252
|
+
self._tbl_md.current_schema_version = self._schema_version_md.preceding_schema_version
|
|
1219
1253
|
|
|
1220
1254
|
conn.execute(
|
|
1221
1255
|
sql.delete(schema.TableVersion.__table__)
|
|
1222
1256
|
.where(schema.TableVersion.tbl_id == self.id)
|
|
1223
1257
|
.where(schema.TableVersion.version == self.version)
|
|
1224
1258
|
)
|
|
1259
|
+
|
|
1225
1260
|
self.version -= 1
|
|
1226
|
-
|
|
1227
|
-
sql.update(schema.Table.__table__)
|
|
1228
|
-
.values({schema.Table.md: dataclasses.asdict(self._create_tbl_md())})
|
|
1229
|
-
.where(schema.Table.id == self.id)
|
|
1230
|
-
)
|
|
1261
|
+
self._write_md(new_version=False, new_version_ts=0, new_schema_version=False)
|
|
1231
1262
|
|
|
1232
1263
|
# propagate to views
|
|
1264
|
+
views_str = ', '.join([str(v.id) for v in self.mutable_views])
|
|
1265
|
+
print(f'revert(): mutable_views={views_str}')
|
|
1233
1266
|
for view in self.mutable_views:
|
|
1234
1267
|
view.get()._revert()
|
|
1268
|
+
|
|
1269
|
+
# force reload on next operation
|
|
1270
|
+
self.is_validated = False
|
|
1271
|
+
pxt.catalog.Catalog.get().remove_tbl_version(self)
|
|
1235
1272
|
_logger.info(f'TableVersion {self.name}: reverted to version {self.version}')
|
|
1236
1273
|
|
|
1237
|
-
def _init_external_stores(self
|
|
1238
|
-
for store_md in tbl_md.external_stores:
|
|
1274
|
+
def _init_external_stores(self) -> None:
|
|
1275
|
+
for store_md in self.tbl_md.external_stores:
|
|
1239
1276
|
store_cls = resolve_symbol(store_md['class'])
|
|
1240
1277
|
assert isinstance(store_cls, type) and issubclass(store_cls, pxt.io.ExternalStore)
|
|
1241
1278
|
store = store_cls.from_dict(store_md['md'])
|
|
1242
1279
|
self.external_stores[store.name] = store
|
|
1243
1280
|
|
|
1244
1281
|
def link_external_store(self, store: pxt.io.ExternalStore) -> None:
|
|
1245
|
-
|
|
1282
|
+
self.version += 1
|
|
1283
|
+
self.preceding_schema_version = self.schema_version
|
|
1284
|
+
self.schema_version = self.version
|
|
1285
|
+
|
|
1246
1286
|
self.external_stores[store.name] = store
|
|
1247
|
-
self.
|
|
1287
|
+
self._tbl_md.external_stores.append(
|
|
1288
|
+
{'class': f'{type(store).__module__}.{type(store).__qualname__}', 'md': store.as_dict()}
|
|
1289
|
+
)
|
|
1290
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
1291
|
+
|
|
1292
|
+
def unlink_external_store(self, store: pxt.io.ExternalStore) -> None:
|
|
1293
|
+
del self.external_stores[store.name]
|
|
1294
|
+
self.version += 1
|
|
1295
|
+
self.preceding_schema_version = self.schema_version
|
|
1296
|
+
self.schema_version = self.version
|
|
1297
|
+
idx = next(i for i, store_md in enumerate(self._tbl_md.external_stores) if store_md['md']['name'] == store.name)
|
|
1298
|
+
self._tbl_md.external_stores.pop(idx)
|
|
1299
|
+
self._write_md(new_version=True, new_version_ts=time.time(), new_schema_version=True)
|
|
1300
|
+
|
|
1301
|
+
@property
|
|
1302
|
+
def tbl_md(self) -> schema.TableMd:
|
|
1303
|
+
return self._tbl_md
|
|
1304
|
+
|
|
1305
|
+
@property
|
|
1306
|
+
def schema_version_md(self) -> schema.TableSchemaVersionMd:
|
|
1307
|
+
return self._schema_version_md
|
|
1308
|
+
|
|
1309
|
+
@property
|
|
1310
|
+
def view_md(self) -> Optional[schema.ViewMd]:
|
|
1311
|
+
return self._tbl_md.view_md
|
|
1312
|
+
|
|
1313
|
+
@property
|
|
1314
|
+
def name(self) -> str:
|
|
1315
|
+
return self._tbl_md.name
|
|
1316
|
+
|
|
1317
|
+
@property
|
|
1318
|
+
def user(self) -> Optional[str]:
|
|
1319
|
+
return self._tbl_md.user
|
|
1320
|
+
|
|
1321
|
+
@property
|
|
1322
|
+
def is_replica(self) -> bool:
|
|
1323
|
+
return self._tbl_md.is_replica
|
|
1324
|
+
|
|
1325
|
+
@property
|
|
1326
|
+
def comment(self) -> str:
|
|
1327
|
+
return self._schema_version_md.comment
|
|
1328
|
+
|
|
1329
|
+
@comment.setter
|
|
1330
|
+
def comment(self, c: str) -> None:
|
|
1331
|
+
assert self.effective_version is None
|
|
1332
|
+
self._schema_version_md.comment = c
|
|
1333
|
+
|
|
1334
|
+
@property
|
|
1335
|
+
def num_retained_versions(self) -> int:
|
|
1336
|
+
return self._schema_version_md.num_retained_versions
|
|
1337
|
+
|
|
1338
|
+
@num_retained_versions.setter
|
|
1339
|
+
def num_retained_versions(self, n: int) -> None:
|
|
1340
|
+
assert self.effective_version is None
|
|
1341
|
+
self._schema_version_md.num_retained_versions = n
|
|
1342
|
+
|
|
1343
|
+
@property
|
|
1344
|
+
def version(self) -> int:
|
|
1345
|
+
# if this is a snapshot instance, we need to ignore current_version
|
|
1346
|
+
return self._tbl_md.current_version if self.effective_version is None else self.effective_version
|
|
1347
|
+
|
|
1348
|
+
@version.setter
|
|
1349
|
+
def version(self, version: int) -> None:
|
|
1350
|
+
assert self.effective_version is None
|
|
1351
|
+
self._tbl_md.current_version = version
|
|
1352
|
+
|
|
1353
|
+
@property
|
|
1354
|
+
def schema_version(self) -> int:
|
|
1355
|
+
return self._schema_version_md.schema_version
|
|
1248
1356
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1357
|
+
@schema_version.setter
|
|
1358
|
+
def schema_version(self, version: int) -> None:
|
|
1359
|
+
assert self.effective_version is None
|
|
1360
|
+
self._tbl_md.current_schema_version = version
|
|
1361
|
+
self._schema_version_md.schema_version = version
|
|
1362
|
+
|
|
1363
|
+
@property
|
|
1364
|
+
def preceding_schema_version(self) -> int:
|
|
1365
|
+
return self._schema_version_md.preceding_schema_version
|
|
1366
|
+
|
|
1367
|
+
@preceding_schema_version.setter
|
|
1368
|
+
def preceding_schema_version(self, v: int) -> None:
|
|
1369
|
+
assert self.effective_version is None
|
|
1370
|
+
self._schema_version_md.preceding_schema_version = v
|
|
1371
|
+
|
|
1372
|
+
@property
|
|
1373
|
+
def media_validation(self) -> MediaValidation:
|
|
1374
|
+
return MediaValidation[self._schema_version_md.media_validation.upper()]
|
|
1375
|
+
|
|
1376
|
+
@property
|
|
1377
|
+
def next_col_id(self) -> int:
|
|
1378
|
+
return self._tbl_md.next_col_id
|
|
1255
1379
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1380
|
+
@next_col_id.setter
|
|
1381
|
+
def next_col_id(self, id: int) -> None:
|
|
1382
|
+
assert self.effective_version is None
|
|
1383
|
+
self._tbl_md.next_col_id = id
|
|
1384
|
+
|
|
1385
|
+
@property
|
|
1386
|
+
def next_idx_id(self) -> int:
|
|
1387
|
+
return self._tbl_md.next_idx_id
|
|
1388
|
+
|
|
1389
|
+
@next_idx_id.setter
|
|
1390
|
+
def next_idx_id(self, id: int) -> None:
|
|
1391
|
+
assert self.effective_version is None
|
|
1392
|
+
self._tbl_md.next_idx_id = id
|
|
1393
|
+
|
|
1394
|
+
@property
|
|
1395
|
+
def next_row_id(self) -> int:
|
|
1396
|
+
return self._tbl_md.next_row_id
|
|
1397
|
+
|
|
1398
|
+
@next_row_id.setter
|
|
1399
|
+
def next_row_id(self, id: int) -> None:
|
|
1400
|
+
assert self.effective_version is None
|
|
1401
|
+
self._tbl_md.next_row_id = id
|
|
1258
1402
|
|
|
1259
1403
|
@property
|
|
1260
1404
|
def is_snapshot(self) -> bool:
|
|
1261
1405
|
return self.effective_version is not None
|
|
1262
1406
|
|
|
1407
|
+
@property
|
|
1408
|
+
def is_mutable(self) -> bool:
|
|
1409
|
+
return not self.is_snapshot and not self.is_replica
|
|
1410
|
+
|
|
1263
1411
|
@property
|
|
1264
1412
|
def is_view(self) -> bool:
|
|
1265
1413
|
return self.view_md is not None
|
|
@@ -1272,6 +1420,7 @@ class TableVersion:
|
|
|
1272
1420
|
def is_component_view(self) -> bool:
|
|
1273
1421
|
return self.iterator_cls is not None
|
|
1274
1422
|
|
|
1423
|
+
@property
|
|
1275
1424
|
def is_insertable(self) -> bool:
|
|
1276
1425
|
"""Returns True if this corresponds to an InsertableTable"""
|
|
1277
1426
|
return not self.is_snapshot and not self.is_view
|
|
@@ -1363,24 +1512,6 @@ class TableVersion:
|
|
|
1363
1512
|
{'class': f'{type(store).__module__}.{type(store).__qualname__}', 'md': store.as_dict()} for store in stores
|
|
1364
1513
|
]
|
|
1365
1514
|
|
|
1366
|
-
def _create_tbl_md(self) -> schema.TableMd:
|
|
1367
|
-
return schema.TableMd(
|
|
1368
|
-
tbl_id=str(self.id),
|
|
1369
|
-
name=self.name,
|
|
1370
|
-
user=self.user,
|
|
1371
|
-
is_replica=self.is_replica,
|
|
1372
|
-
current_version=self.version,
|
|
1373
|
-
current_schema_version=self.schema_version,
|
|
1374
|
-
next_col_id=self.next_col_id,
|
|
1375
|
-
next_idx_id=self.next_idx_id,
|
|
1376
|
-
next_row_id=self.next_rowid,
|
|
1377
|
-
column_md=self._create_column_md(self.cols),
|
|
1378
|
-
index_md=self.idx_md,
|
|
1379
|
-
external_stores=self._create_stores_md(self.external_stores.values()),
|
|
1380
|
-
view_md=self.view_md,
|
|
1381
|
-
additional_md={},
|
|
1382
|
-
)
|
|
1383
|
-
|
|
1384
1515
|
def _create_version_md(self, timestamp: float) -> schema.TableVersionMd:
|
|
1385
1516
|
return schema.TableVersionMd(
|
|
1386
1517
|
tbl_id=str(self.id),
|