pixeltable 0.3.1__py3-none-any.whl → 0.3.3__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/__init__.py +64 -11
- pixeltable/__version__.py +2 -2
- pixeltable/catalog/__init__.py +1 -1
- pixeltable/catalog/catalog.py +50 -27
- pixeltable/catalog/column.py +27 -11
- pixeltable/catalog/dir.py +6 -4
- pixeltable/catalog/globals.py +8 -1
- pixeltable/catalog/insertable_table.py +25 -15
- pixeltable/catalog/named_function.py +10 -6
- pixeltable/catalog/path.py +3 -2
- pixeltable/catalog/path_dict.py +8 -6
- pixeltable/catalog/schema_object.py +2 -1
- pixeltable/catalog/table.py +123 -103
- pixeltable/catalog/table_version.py +292 -143
- pixeltable/catalog/table_version_path.py +8 -5
- pixeltable/catalog/view.py +68 -27
- pixeltable/dataframe.py +102 -72
- pixeltable/env.py +39 -23
- pixeltable/exec/__init__.py +2 -2
- pixeltable/exec/aggregation_node.py +10 -4
- pixeltable/exec/cache_prefetch_node.py +5 -3
- pixeltable/exec/component_iteration_node.py +9 -8
- pixeltable/exec/data_row_batch.py +21 -10
- pixeltable/exec/exec_context.py +10 -3
- pixeltable/exec/exec_node.py +23 -12
- pixeltable/exec/expr_eval/evaluators.py +18 -17
- pixeltable/exec/expr_eval/expr_eval_node.py +29 -16
- pixeltable/exec/expr_eval/globals.py +33 -11
- pixeltable/exec/expr_eval/row_buffer.py +5 -6
- pixeltable/exec/expr_eval/schedulers.py +170 -42
- pixeltable/exec/in_memory_data_node.py +8 -7
- pixeltable/exec/row_update_node.py +15 -5
- pixeltable/exec/sql_node.py +56 -27
- pixeltable/exprs/__init__.py +2 -2
- pixeltable/exprs/arithmetic_expr.py +57 -26
- pixeltable/exprs/array_slice.py +1 -1
- pixeltable/exprs/column_property_ref.py +2 -1
- pixeltable/exprs/column_ref.py +20 -15
- pixeltable/exprs/comparison.py +6 -2
- pixeltable/exprs/compound_predicate.py +1 -3
- pixeltable/exprs/data_row.py +2 -2
- pixeltable/exprs/expr.py +101 -72
- pixeltable/exprs/expr_dict.py +2 -1
- pixeltable/exprs/expr_set.py +3 -1
- pixeltable/exprs/function_call.py +39 -41
- pixeltable/exprs/globals.py +1 -0
- pixeltable/exprs/in_predicate.py +2 -2
- pixeltable/exprs/inline_expr.py +20 -17
- pixeltable/exprs/json_mapper.py +4 -2
- pixeltable/exprs/json_path.py +12 -18
- pixeltable/exprs/literal.py +5 -9
- pixeltable/exprs/method_ref.py +1 -0
- pixeltable/exprs/object_ref.py +1 -1
- pixeltable/exprs/row_builder.py +31 -16
- pixeltable/exprs/rowid_ref.py +14 -5
- pixeltable/exprs/similarity_expr.py +11 -6
- pixeltable/exprs/sql_element_cache.py +1 -1
- pixeltable/exprs/type_cast.py +24 -9
- pixeltable/ext/__init__.py +1 -0
- pixeltable/ext/functions/__init__.py +1 -0
- pixeltable/ext/functions/whisperx.py +2 -2
- pixeltable/ext/functions/yolox.py +11 -11
- pixeltable/func/aggregate_function.py +17 -13
- pixeltable/func/callable_function.py +6 -6
- pixeltable/func/expr_template_function.py +15 -14
- pixeltable/func/function.py +16 -16
- pixeltable/func/function_registry.py +11 -8
- pixeltable/func/globals.py +4 -2
- pixeltable/func/query_template_function.py +12 -13
- pixeltable/func/signature.py +18 -9
- pixeltable/func/tools.py +10 -17
- pixeltable/func/udf.py +106 -11
- pixeltable/functions/__init__.py +21 -2
- pixeltable/functions/anthropic.py +21 -15
- pixeltable/functions/fireworks.py +63 -5
- pixeltable/functions/gemini.py +13 -3
- pixeltable/functions/globals.py +18 -6
- pixeltable/functions/huggingface.py +20 -38
- pixeltable/functions/image.py +7 -3
- pixeltable/functions/json.py +1 -0
- pixeltable/functions/llama_cpp.py +1 -4
- pixeltable/functions/mistralai.py +31 -20
- pixeltable/functions/ollama.py +4 -18
- pixeltable/functions/openai.py +214 -109
- pixeltable/functions/replicate.py +11 -10
- pixeltable/functions/string.py +70 -7
- pixeltable/functions/timestamp.py +21 -8
- pixeltable/functions/together.py +66 -52
- pixeltable/functions/video.py +1 -0
- pixeltable/functions/vision.py +14 -11
- pixeltable/functions/whisper.py +2 -1
- pixeltable/globals.py +61 -28
- pixeltable/index/__init__.py +1 -1
- pixeltable/index/btree.py +5 -3
- pixeltable/index/embedding_index.py +15 -14
- pixeltable/io/__init__.py +1 -1
- pixeltable/io/external_store.py +30 -25
- pixeltable/io/fiftyone.py +6 -14
- pixeltable/io/globals.py +33 -27
- pixeltable/io/hf_datasets.py +3 -2
- pixeltable/io/label_studio.py +80 -71
- pixeltable/io/pandas.py +33 -9
- pixeltable/io/parquet.py +10 -13
- pixeltable/iterators/__init__.py +1 -0
- pixeltable/iterators/audio.py +205 -0
- pixeltable/iterators/document.py +19 -8
- pixeltable/iterators/image.py +6 -24
- pixeltable/iterators/string.py +3 -6
- pixeltable/iterators/video.py +1 -7
- pixeltable/metadata/__init__.py +9 -2
- pixeltable/metadata/converters/convert_10.py +2 -2
- pixeltable/metadata/converters/convert_15.py +1 -5
- pixeltable/metadata/converters/convert_16.py +2 -4
- pixeltable/metadata/converters/convert_17.py +2 -4
- pixeltable/metadata/converters/convert_18.py +2 -4
- pixeltable/metadata/converters/convert_19.py +2 -5
- pixeltable/metadata/converters/convert_20.py +1 -4
- pixeltable/metadata/converters/convert_21.py +4 -6
- pixeltable/metadata/converters/convert_22.py +1 -0
- pixeltable/metadata/converters/convert_23.py +5 -5
- pixeltable/metadata/converters/convert_24.py +12 -13
- pixeltable/metadata/converters/convert_26.py +23 -0
- pixeltable/metadata/converters/util.py +3 -4
- pixeltable/metadata/notes.py +1 -0
- pixeltable/metadata/schema.py +13 -2
- pixeltable/plan.py +173 -98
- pixeltable/store.py +42 -26
- pixeltable/type_system.py +130 -85
- pixeltable/utils/arrow.py +1 -7
- pixeltable/utils/coco.py +16 -17
- pixeltable/utils/code.py +1 -1
- pixeltable/utils/console_output.py +44 -0
- pixeltable/utils/description_helper.py +7 -7
- pixeltable/utils/documents.py +3 -1
- pixeltable/utils/filecache.py +13 -8
- pixeltable/utils/http_server.py +9 -8
- pixeltable/utils/media_store.py +2 -1
- pixeltable/utils/pytorch.py +11 -14
- pixeltable/utils/s3.py +1 -0
- pixeltable/utils/sql.py +1 -0
- pixeltable/utils/transactional_directory.py +2 -2
- {pixeltable-0.3.1.dist-info → pixeltable-0.3.3.dist-info}/METADATA +7 -8
- pixeltable-0.3.3.dist-info/RECORD +163 -0
- pixeltable-0.3.1.dist-info/RECORD +0 -160
- {pixeltable-0.3.1.dist-info → pixeltable-0.3.3.dist-info}/LICENSE +0 -0
- {pixeltable-0.3.1.dist-info → pixeltable-0.3.3.dist-info}/WHEEL +0 -0
- {pixeltable-0.3.1.dist-info → pixeltable-0.3.3.dist-info}/entry_points.txt +0 -0
|
@@ -27,7 +27,7 @@ from pixeltable.utils.media_store import MediaStore
|
|
|
27
27
|
|
|
28
28
|
from ..func.globals import resolve_symbol
|
|
29
29
|
from .column import Column
|
|
30
|
-
from .globals import _POS_COLUMN_NAME, _ROWID_COLUMN_NAME, UpdateStatus, is_valid_identifier
|
|
30
|
+
from .globals import _POS_COLUMN_NAME, _ROWID_COLUMN_NAME, MediaValidation, UpdateStatus, is_valid_identifier
|
|
31
31
|
|
|
32
32
|
if TYPE_CHECKING:
|
|
33
33
|
from pixeltable import exec, store
|
|
@@ -94,11 +94,15 @@ class TableVersion:
|
|
|
94
94
|
val_col: Column
|
|
95
95
|
undo_col: Column
|
|
96
96
|
|
|
97
|
-
|
|
98
97
|
def __init__(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
self,
|
|
99
|
+
id: UUID,
|
|
100
|
+
tbl_md: schema.TableMd,
|
|
101
|
+
version: int,
|
|
102
|
+
schema_version_md: schema.TableSchemaVersionMd,
|
|
103
|
+
base: Optional[TableVersion] = None,
|
|
104
|
+
base_path: Optional[pxt.catalog.TableVersionPath] = None,
|
|
105
|
+
is_snapshot: Optional[bool] = None,
|
|
102
106
|
):
|
|
103
107
|
# only one of base and base_path can be non-None
|
|
104
108
|
assert base is None or base_path is None
|
|
@@ -117,6 +121,7 @@ class TableVersion:
|
|
|
117
121
|
|
|
118
122
|
# mutable tables need their TableVersionPath for expr eval during updates
|
|
119
123
|
from .table_version_path import TableVersionPath
|
|
124
|
+
|
|
120
125
|
if self.is_snapshot:
|
|
121
126
|
self.path = None
|
|
122
127
|
else:
|
|
@@ -135,6 +140,7 @@ class TableVersion:
|
|
|
135
140
|
|
|
136
141
|
# view-specific initialization
|
|
137
142
|
from pixeltable import exprs
|
|
143
|
+
|
|
138
144
|
predicate_dict = None if not is_view or tbl_md.view_md.predicate is None else tbl_md.view_md.predicate
|
|
139
145
|
self.predicate = exprs.Expr.from_dict(predicate_dict) if predicate_dict is not None else None
|
|
140
146
|
self.mutable_views = [] # targets for update propagation
|
|
@@ -186,15 +192,26 @@ class TableVersion:
|
|
|
186
192
|
"""Create a snapshot copy of this TableVersion"""
|
|
187
193
|
assert not self.is_snapshot
|
|
188
194
|
return TableVersion(
|
|
189
|
-
self.id,
|
|
195
|
+
self.id,
|
|
196
|
+
self._create_tbl_md(),
|
|
197
|
+
self.version,
|
|
190
198
|
self._create_schema_version_md(preceding_schema_version=0), # preceding_schema_version: dummy value
|
|
191
|
-
is_snapshot=True,
|
|
199
|
+
is_snapshot=True,
|
|
200
|
+
base=self.base,
|
|
201
|
+
)
|
|
192
202
|
|
|
193
203
|
@classmethod
|
|
194
204
|
def create(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
205
|
+
cls,
|
|
206
|
+
session: orm.Session,
|
|
207
|
+
dir_id: UUID,
|
|
208
|
+
name: str,
|
|
209
|
+
cols: list[Column],
|
|
210
|
+
num_retained_versions: int,
|
|
211
|
+
comment: str,
|
|
212
|
+
media_validation: MediaValidation,
|
|
213
|
+
base_path: Optional[pxt.catalog.TableVersionPath] = None,
|
|
214
|
+
view_md: Optional[schema.ViewMd] = None,
|
|
198
215
|
) -> tuple[UUID, Optional[TableVersion]]:
|
|
199
216
|
# assign ids
|
|
200
217
|
cols_by_name: dict[str, Column] = {}
|
|
@@ -210,8 +227,17 @@ class TableVersion:
|
|
|
210
227
|
# Column.dependent_cols for existing cols is wrong at this point, but init() will set it correctly
|
|
211
228
|
column_md = cls._create_column_md(cols)
|
|
212
229
|
table_md = schema.TableMd(
|
|
213
|
-
name=name,
|
|
214
|
-
|
|
230
|
+
name=name,
|
|
231
|
+
current_version=0,
|
|
232
|
+
current_schema_version=0,
|
|
233
|
+
next_col_id=len(cols),
|
|
234
|
+
next_idx_id=0,
|
|
235
|
+
next_row_id=0,
|
|
236
|
+
column_md=column_md,
|
|
237
|
+
index_md={},
|
|
238
|
+
external_stores=[],
|
|
239
|
+
view_md=view_md,
|
|
240
|
+
)
|
|
215
241
|
# create a schema.Table here, we need it to call our c'tor;
|
|
216
242
|
# don't add it to the session yet, we might add index metadata
|
|
217
243
|
tbl_id = uuid.uuid4()
|
|
@@ -220,22 +246,30 @@ class TableVersion:
|
|
|
220
246
|
# create schema.TableVersion
|
|
221
247
|
table_version_md = schema.TableVersionMd(created_at=timestamp, version=0, schema_version=0)
|
|
222
248
|
tbl_version_record = schema.TableVersion(
|
|
223
|
-
tbl_id=tbl_record.id, version=0, md=dataclasses.asdict(table_version_md)
|
|
249
|
+
tbl_id=tbl_record.id, version=0, md=dataclasses.asdict(table_version_md)
|
|
250
|
+
)
|
|
224
251
|
|
|
225
252
|
# create schema.TableSchemaVersion
|
|
226
253
|
schema_col_md: dict[int, schema.SchemaColumn] = {}
|
|
227
254
|
for pos, col in enumerate(cols):
|
|
228
255
|
md = schema.SchemaColumn(
|
|
229
|
-
pos=pos,
|
|
230
|
-
|
|
256
|
+
pos=pos,
|
|
257
|
+
name=col.name,
|
|
258
|
+
media_validation=col._media_validation.name.lower() if col._media_validation is not None else None,
|
|
259
|
+
)
|
|
231
260
|
schema_col_md[col.id] = md
|
|
232
261
|
|
|
233
262
|
schema_version_md = schema.TableSchemaVersionMd(
|
|
234
|
-
schema_version=0,
|
|
235
|
-
|
|
236
|
-
|
|
263
|
+
schema_version=0,
|
|
264
|
+
preceding_schema_version=None,
|
|
265
|
+
columns=schema_col_md,
|
|
266
|
+
num_retained_versions=num_retained_versions,
|
|
267
|
+
comment=comment,
|
|
268
|
+
media_validation=media_validation.name.lower(),
|
|
269
|
+
)
|
|
237
270
|
schema_version_record = schema.TableSchemaVersion(
|
|
238
|
-
tbl_id=tbl_record.id, schema_version=0, md=dataclasses.asdict(schema_version_md)
|
|
271
|
+
tbl_id=tbl_record.id, schema_version=0, md=dataclasses.asdict(schema_version_md)
|
|
272
|
+
)
|
|
239
273
|
|
|
240
274
|
# if this is purely a snapshot (it doesn't require any additional storage for columns and it # doesn't have a
|
|
241
275
|
# predicate to apply at runtime), we don't create a physical table and simply use the base's table version path
|
|
@@ -267,10 +301,8 @@ class TableVersion:
|
|
|
267
301
|
|
|
268
302
|
@classmethod
|
|
269
303
|
def delete_md(cls, tbl_id: UUID, conn: sql.Connection) -> None:
|
|
270
|
-
conn.execute(
|
|
271
|
-
|
|
272
|
-
conn.execute(
|
|
273
|
-
sql.delete(schema.TableVersion.__table__).where(schema.TableVersion.tbl_id == tbl_id))
|
|
304
|
+
conn.execute(sql.delete(schema.TableSchemaVersion.__table__).where(schema.TableSchemaVersion.tbl_id == tbl_id))
|
|
305
|
+
conn.execute(sql.delete(schema.TableVersion.__table__).where(schema.TableVersion.tbl_id == tbl_id))
|
|
274
306
|
conn.execute(sql.delete(schema.Table.__table__).where(schema.Table.id == tbl_id))
|
|
275
307
|
|
|
276
308
|
def drop(self) -> None:
|
|
@@ -283,6 +315,7 @@ class TableVersion:
|
|
|
283
315
|
|
|
284
316
|
# de-register table version from catalog
|
|
285
317
|
from .catalog import Catalog
|
|
318
|
+
|
|
286
319
|
cat = Catalog.get()
|
|
287
320
|
del cat.tbl_versions[(self.id, self.effective_version)]
|
|
288
321
|
# TODO: remove from tbl_dependents
|
|
@@ -304,13 +337,20 @@ class TableVersion:
|
|
|
304
337
|
col_name = schema_col_md.name if schema_col_md is not None else None
|
|
305
338
|
media_val = (
|
|
306
339
|
MediaValidation[schema_col_md.media_validation.upper()]
|
|
307
|
-
if schema_col_md is not None and schema_col_md.media_validation is not None
|
|
340
|
+
if schema_col_md is not None and schema_col_md.media_validation is not None
|
|
341
|
+
else None
|
|
308
342
|
)
|
|
309
343
|
col = Column(
|
|
310
|
-
col_id=col_md.id,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
344
|
+
col_id=col_md.id,
|
|
345
|
+
name=col_name,
|
|
346
|
+
col_type=ts.ColumnType.from_dict(col_md.col_type),
|
|
347
|
+
is_pk=col_md.is_pk,
|
|
348
|
+
stored=col_md.stored,
|
|
349
|
+
media_validation=media_val,
|
|
350
|
+
schema_version_add=col_md.schema_version_add,
|
|
351
|
+
schema_version_drop=col_md.schema_version_drop,
|
|
352
|
+
value_expr_dict=col_md.value_expr,
|
|
353
|
+
)
|
|
314
354
|
col.tbl = self
|
|
315
355
|
self.cols.append(col)
|
|
316
356
|
|
|
@@ -335,9 +375,13 @@ class TableVersion:
|
|
|
335
375
|
self.idx_md = tbl_md.index_md
|
|
336
376
|
self.idxs_by_name = {}
|
|
337
377
|
import pixeltable.index as index_module
|
|
378
|
+
|
|
338
379
|
for md in tbl_md.index_md.values():
|
|
339
|
-
if
|
|
340
|
-
|
|
380
|
+
if (
|
|
381
|
+
md.schema_version_add > self.schema_version
|
|
382
|
+
or md.schema_version_drop is not None
|
|
383
|
+
and md.schema_version_drop <= self.schema_version
|
|
384
|
+
):
|
|
341
385
|
# index not visible in this schema version
|
|
342
386
|
continue
|
|
343
387
|
|
|
@@ -361,6 +405,7 @@ class TableVersion:
|
|
|
361
405
|
# create the sqlalchemy schema; do this after instantiating columns, in order to determine whether they
|
|
362
406
|
# need to record errors
|
|
363
407
|
from pixeltable.store import StoreComponentView, StoreTable, StoreView
|
|
408
|
+
|
|
364
409
|
if self.is_component_view():
|
|
365
410
|
self.store_tbl = StoreComponentView(self)
|
|
366
411
|
elif self.is_view():
|
|
@@ -369,8 +414,11 @@ class TableVersion:
|
|
|
369
414
|
self.store_tbl = StoreTable(self)
|
|
370
415
|
|
|
371
416
|
def _update_md(
|
|
372
|
-
self,
|
|
373
|
-
|
|
417
|
+
self,
|
|
418
|
+
timestamp: float,
|
|
419
|
+
conn: sql.engine.Connection,
|
|
420
|
+
update_tbl_version: bool = True,
|
|
421
|
+
preceding_schema_version: Optional[int] = None,
|
|
374
422
|
) -> None:
|
|
375
423
|
"""Writes table metadata to the database.
|
|
376
424
|
|
|
@@ -385,22 +433,25 @@ class TableVersion:
|
|
|
385
433
|
|
|
386
434
|
conn.execute(
|
|
387
435
|
sql.update(schema.Table.__table__)
|
|
388
|
-
|
|
389
|
-
|
|
436
|
+
.values({schema.Table.md: dataclasses.asdict(self._create_tbl_md())})
|
|
437
|
+
.where(schema.Table.id == self.id)
|
|
438
|
+
)
|
|
390
439
|
|
|
391
440
|
if update_tbl_version:
|
|
392
441
|
version_md = self._create_version_md(timestamp)
|
|
393
442
|
conn.execute(
|
|
394
|
-
sql.insert(schema.TableVersion.__table__)
|
|
395
|
-
|
|
443
|
+
sql.insert(schema.TableVersion.__table__).values(
|
|
444
|
+
tbl_id=self.id, version=self.version, md=dataclasses.asdict(version_md)
|
|
445
|
+
)
|
|
446
|
+
)
|
|
396
447
|
|
|
397
448
|
if preceding_schema_version is not None:
|
|
398
449
|
schema_version_md = self._create_schema_version_md(preceding_schema_version)
|
|
399
450
|
conn.execute(
|
|
400
|
-
sql.insert(schema.TableSchemaVersion.__table__)
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
451
|
+
sql.insert(schema.TableSchemaVersion.__table__).values(
|
|
452
|
+
tbl_id=self.id, schema_version=self.schema_version, md=dataclasses.asdict(schema_version_md)
|
|
453
|
+
)
|
|
454
|
+
)
|
|
404
455
|
|
|
405
456
|
def _store_idx_name(self, idx_id: int) -> str:
|
|
406
457
|
"""Return name of index in the store, which needs to be globally unique"""
|
|
@@ -422,6 +473,9 @@ class TableVersion:
|
|
|
422
473
|
if not col.stored:
|
|
423
474
|
# if the column is intentionally not stored, we want to avoid the overhead of an index
|
|
424
475
|
return None
|
|
476
|
+
# Skip index for stored media columns produced by an iterator
|
|
477
|
+
if col.col_type.is_media_type() and self.is_iterator_column(col):
|
|
478
|
+
return None
|
|
425
479
|
if not col.col_type.is_scalar_type() and not (col.col_type.is_media_type() and not col.is_computed):
|
|
426
480
|
# wrong type for a B-tree
|
|
427
481
|
return None
|
|
@@ -432,7 +486,7 @@ class TableVersion:
|
|
|
432
486
|
return status
|
|
433
487
|
|
|
434
488
|
def _add_index(
|
|
435
|
-
|
|
489
|
+
self, col: Column, idx_name: Optional[str], idx: index.IndexBase, conn: sql.engine.Connection
|
|
436
490
|
) -> UpdateStatus:
|
|
437
491
|
assert not self.is_snapshot
|
|
438
492
|
idx_id = self.next_idx_id
|
|
@@ -445,19 +499,29 @@ class TableVersion:
|
|
|
445
499
|
|
|
446
500
|
# add the index value and undo columns (which need to be nullable)
|
|
447
501
|
val_col = Column(
|
|
448
|
-
col_id=self.next_col_id,
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
502
|
+
col_id=self.next_col_id,
|
|
503
|
+
name=None,
|
|
504
|
+
computed_with=idx.index_value_expr(),
|
|
505
|
+
sa_col_type=idx.index_sa_type(),
|
|
506
|
+
stored=True,
|
|
507
|
+
schema_version_add=self.schema_version,
|
|
508
|
+
schema_version_drop=None,
|
|
509
|
+
records_errors=idx.records_value_errors(),
|
|
510
|
+
)
|
|
452
511
|
val_col.tbl = self
|
|
453
512
|
val_col.col_type = val_col.col_type.copy(nullable=True)
|
|
454
513
|
self.next_col_id += 1
|
|
455
514
|
|
|
456
515
|
undo_col = Column(
|
|
457
|
-
col_id=self.next_col_id,
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
516
|
+
col_id=self.next_col_id,
|
|
517
|
+
name=None,
|
|
518
|
+
col_type=val_col.col_type,
|
|
519
|
+
sa_col_type=val_col.sa_col_type,
|
|
520
|
+
stored=True,
|
|
521
|
+
schema_version_add=self.schema_version,
|
|
522
|
+
schema_version_drop=None,
|
|
523
|
+
records_errors=False,
|
|
524
|
+
)
|
|
461
525
|
undo_col.tbl = self
|
|
462
526
|
undo_col.col_type = undo_col.col_type.copy(nullable=True)
|
|
463
527
|
self.next_col_id += 1
|
|
@@ -465,11 +529,17 @@ class TableVersion:
|
|
|
465
529
|
# create and register the index metadata
|
|
466
530
|
idx_cls = type(idx)
|
|
467
531
|
idx_md = schema.IndexMd(
|
|
468
|
-
id=idx_id,
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
532
|
+
id=idx_id,
|
|
533
|
+
name=idx_name,
|
|
534
|
+
indexed_col_id=col.id,
|
|
535
|
+
indexed_col_tbl_id=str(col.tbl.id),
|
|
536
|
+
index_val_col_id=val_col.id,
|
|
537
|
+
index_val_undo_col_id=undo_col.id,
|
|
538
|
+
schema_version_add=self.schema_version,
|
|
539
|
+
schema_version_drop=None,
|
|
540
|
+
class_fqn=idx_cls.__module__ + '.' + idx_cls.__name__,
|
|
541
|
+
init_args=idx.as_dict(),
|
|
542
|
+
)
|
|
473
543
|
idx_info = self.IndexInfo(id=idx_id, name=idx_name, idx=idx, col=col, val_col=val_col, undo_col=undo_col)
|
|
474
544
|
self.idx_md[idx_id] = idx_md
|
|
475
545
|
self.idxs_by_name[idx_name] = idx_info
|
|
@@ -505,9 +575,10 @@ class TableVersion:
|
|
|
505
575
|
self._update_md(time.time(), conn, preceding_schema_version=preceding_schema_version)
|
|
506
576
|
_logger.info(f'Dropped index {idx_md.name} on table {self.name}')
|
|
507
577
|
|
|
508
|
-
def add_columns(
|
|
509
|
-
|
|
510
|
-
|
|
578
|
+
def add_columns(
|
|
579
|
+
self, cols: Iterable[Column], print_stats: bool, on_error: Literal['abort', 'ignore']
|
|
580
|
+
) -> UpdateStatus:
|
|
581
|
+
"""Adds a column to the table."""
|
|
511
582
|
assert not self.is_snapshot
|
|
512
583
|
assert all(is_valid_identifier(col.name) for col in cols)
|
|
513
584
|
assert all(col.stored is not None for col in cols)
|
|
@@ -532,7 +603,7 @@ class TableVersion:
|
|
|
532
603
|
f'Added {status.num_rows} column value{"" if status.num_rows == 1 else "s"} '
|
|
533
604
|
f'with {status.num_excs} error{"" if status.num_excs == 1 else "s"}.'
|
|
534
605
|
)
|
|
535
|
-
|
|
606
|
+
Env.get().console_logger.info(msg)
|
|
536
607
|
_logger.info(f'Columns {[col.name for col in cols]}: {msg}')
|
|
537
608
|
return status
|
|
538
609
|
|
|
@@ -541,7 +612,7 @@ class TableVersion:
|
|
|
541
612
|
cols: Iterable[Column],
|
|
542
613
|
conn: sql.engine.Connection,
|
|
543
614
|
print_stats: bool,
|
|
544
|
-
on_error: Literal['abort', 'ignore']
|
|
615
|
+
on_error: Literal['abort', 'ignore'],
|
|
545
616
|
) -> UpdateStatus:
|
|
546
617
|
"""Add and populate columns within the current transaction"""
|
|
547
618
|
cols = list(cols)
|
|
@@ -550,7 +621,8 @@ class TableVersion:
|
|
|
550
621
|
if not col.col_type.nullable and not col.is_computed:
|
|
551
622
|
if row_count > 0:
|
|
552
623
|
raise excs.Error(
|
|
553
|
-
f'Cannot add non-nullable column "{col.name}" to table {self.name} with existing rows'
|
|
624
|
+
f'Cannot add non-nullable column "{col.name}" to table {self.name} with existing rows'
|
|
625
|
+
)
|
|
554
626
|
|
|
555
627
|
num_excs = 0
|
|
556
628
|
cols_with_excs: list[Column] = []
|
|
@@ -574,6 +646,7 @@ class TableVersion:
|
|
|
574
646
|
|
|
575
647
|
# populate the column
|
|
576
648
|
from pixeltable.plan import Planner
|
|
649
|
+
|
|
577
650
|
plan, value_expr_slot_idx = Planner.create_add_column_plan(self.path, col)
|
|
578
651
|
plan.ctx.num_rows = row_count
|
|
579
652
|
|
|
@@ -606,12 +679,14 @@ class TableVersion:
|
|
|
606
679
|
plan.ctx.profile.print(num_rows=row_count)
|
|
607
680
|
# TODO(mkornacker): what to do about system columns with exceptions?
|
|
608
681
|
return UpdateStatus(
|
|
609
|
-
num_rows=row_count,
|
|
610
|
-
|
|
682
|
+
num_rows=row_count,
|
|
683
|
+
num_computed_values=row_count,
|
|
684
|
+
num_excs=num_excs,
|
|
685
|
+
cols_with_excs=[f'{col.tbl.name}.{col.name}' for col in cols_with_excs if col.name is not None],
|
|
686
|
+
)
|
|
611
687
|
|
|
612
688
|
def drop_column(self, col: Column) -> None:
|
|
613
|
-
"""Drop a column from the table.
|
|
614
|
-
"""
|
|
689
|
+
"""Drop a column from the table."""
|
|
615
690
|
from pixeltable.catalog import Catalog
|
|
616
691
|
|
|
617
692
|
assert not self.is_snapshot
|
|
@@ -662,8 +737,7 @@ class TableVersion:
|
|
|
662
737
|
self.store_tbl.create_sa_tbl()
|
|
663
738
|
|
|
664
739
|
def rename_column(self, old_name: str, new_name: str) -> None:
|
|
665
|
-
"""Rename a column.
|
|
666
|
-
"""
|
|
740
|
+
"""Rename a column."""
|
|
667
741
|
assert not self.is_snapshot
|
|
668
742
|
if old_name not in self.cols_by_name:
|
|
669
743
|
raise excs.Error(f'Unknown column: {old_name}')
|
|
@@ -691,7 +765,9 @@ class TableVersion:
|
|
|
691
765
|
self._create_schema_version()
|
|
692
766
|
|
|
693
767
|
def set_num_retained_versions(self, new_num_retained_versions: int):
|
|
694
|
-
_logger.info(
|
|
768
|
+
_logger.info(
|
|
769
|
+
f'[{self.name}] Updating num_retained_versions: {new_num_retained_versions} (was {self.num_retained_versions})'
|
|
770
|
+
)
|
|
695
771
|
self.num_retained_versions = new_num_retained_versions
|
|
696
772
|
self._create_schema_version()
|
|
697
773
|
|
|
@@ -705,12 +781,12 @@ class TableVersion:
|
|
|
705
781
|
_logger.info(f'[{self.name}] Updating table schema to version: {self.version}')
|
|
706
782
|
|
|
707
783
|
def insert(
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
784
|
+
self,
|
|
785
|
+
rows: Optional[list[dict[str, Any]]],
|
|
786
|
+
df: Optional[pxt.DataFrame],
|
|
787
|
+
conn: Optional[sql.engine.Connection] = None,
|
|
788
|
+
print_stats: bool = False,
|
|
789
|
+
fail_on_exception: bool = True,
|
|
714
790
|
) -> UpdateStatus:
|
|
715
791
|
"""
|
|
716
792
|
Insert rows into this table, either from an explicit list of dicts or from a `DataFrame`.
|
|
@@ -734,22 +810,30 @@ class TableVersion:
|
|
|
734
810
|
if conn is None:
|
|
735
811
|
with Env.get().engine.begin() as conn:
|
|
736
812
|
return self._insert(
|
|
737
|
-
plan, conn, time.time(), print_stats=print_stats, rowids=rowids(),
|
|
738
|
-
|
|
813
|
+
plan, conn, time.time(), print_stats=print_stats, rowids=rowids(), abort_on_exc=fail_on_exception
|
|
814
|
+
)
|
|
739
815
|
else:
|
|
740
816
|
return self._insert(
|
|
741
|
-
plan, conn, time.time(), print_stats=print_stats, rowids=rowids(), abort_on_exc=fail_on_exception
|
|
817
|
+
plan, conn, time.time(), print_stats=print_stats, rowids=rowids(), abort_on_exc=fail_on_exception
|
|
818
|
+
)
|
|
742
819
|
|
|
743
820
|
def _insert(
|
|
744
|
-
self,
|
|
745
|
-
|
|
821
|
+
self,
|
|
822
|
+
exec_plan: 'exec.ExecNode',
|
|
823
|
+
conn: sql.engine.Connection,
|
|
824
|
+
timestamp: float,
|
|
825
|
+
*,
|
|
826
|
+
rowids: Optional[Iterator[int]] = None,
|
|
827
|
+
print_stats: bool = False,
|
|
828
|
+
abort_on_exc: bool = False,
|
|
746
829
|
) -> UpdateStatus:
|
|
747
830
|
"""Insert rows produced by exec_plan and propagate to views"""
|
|
748
831
|
# we're creating a new version
|
|
749
832
|
self.version += 1
|
|
750
833
|
result = UpdateStatus()
|
|
751
834
|
num_rows, num_excs, cols_with_excs = self.store_tbl.insert_rows(
|
|
752
|
-
exec_plan, conn, v_min=self.version, rowids=rowids, abort_on_exc=abort_on_exc
|
|
835
|
+
exec_plan, conn, v_min=self.version, rowids=rowids, abort_on_exc=abort_on_exc
|
|
836
|
+
)
|
|
753
837
|
result.num_rows = num_rows
|
|
754
838
|
result.num_excs = num_excs
|
|
755
839
|
result.num_computed_values += exec_plan.ctx.num_computed_exprs * num_rows
|
|
@@ -759,6 +843,7 @@ class TableVersion:
|
|
|
759
843
|
# update views
|
|
760
844
|
for view in self.mutable_views:
|
|
761
845
|
from pixeltable.plan import Planner
|
|
846
|
+
|
|
762
847
|
plan, _ = Planner.create_view_load_plan(view.path, propagates_insert=True)
|
|
763
848
|
status = view._insert(plan, conn, timestamp, print_stats=print_stats)
|
|
764
849
|
result.num_rows += status.num_rows
|
|
@@ -799,15 +884,27 @@ class TableVersion:
|
|
|
799
884
|
with Env.get().engine.begin() as conn:
|
|
800
885
|
plan, updated_cols, recomputed_cols = Planner.create_update_plan(self.path, update_spec, [], where, cascade)
|
|
801
886
|
from pixeltable.exprs import SqlElementCache
|
|
887
|
+
|
|
802
888
|
result = self.propagate_update(
|
|
803
|
-
plan,
|
|
804
|
-
|
|
889
|
+
plan,
|
|
890
|
+
where.sql_expr(SqlElementCache()) if where is not None else None,
|
|
891
|
+
recomputed_cols,
|
|
892
|
+
base_versions=[],
|
|
893
|
+
conn=conn,
|
|
894
|
+
timestamp=time.time(),
|
|
895
|
+
cascade=cascade,
|
|
896
|
+
show_progress=True,
|
|
897
|
+
)
|
|
805
898
|
result.updated_cols = updated_cols
|
|
806
899
|
return result
|
|
807
900
|
|
|
808
901
|
def batch_update(
|
|
809
|
-
|
|
810
|
-
|
|
902
|
+
self,
|
|
903
|
+
batch: list[dict[Column, exprs.Expr]],
|
|
904
|
+
rowids: list[tuple[int, ...]],
|
|
905
|
+
insert_if_not_exists: bool,
|
|
906
|
+
error_if_not_exists: bool,
|
|
907
|
+
cascade: bool = True,
|
|
811
908
|
) -> UpdateStatus:
|
|
812
909
|
"""Update rows in batch.
|
|
813
910
|
Args:
|
|
@@ -821,11 +918,18 @@ class TableVersion:
|
|
|
821
918
|
with Env.get().engine.begin() as conn:
|
|
822
919
|
from pixeltable.plan import Planner
|
|
823
920
|
|
|
824
|
-
plan, row_update_node, delete_where_clause, updated_cols, recomputed_cols =
|
|
921
|
+
plan, row_update_node, delete_where_clause, updated_cols, recomputed_cols = (
|
|
825
922
|
Planner.create_batch_update_plan(self.path, batch, rowids, cascade=cascade)
|
|
923
|
+
)
|
|
826
924
|
result = self.propagate_update(
|
|
827
|
-
plan,
|
|
828
|
-
|
|
925
|
+
plan,
|
|
926
|
+
delete_where_clause,
|
|
927
|
+
recomputed_cols,
|
|
928
|
+
base_versions=[],
|
|
929
|
+
conn=conn,
|
|
930
|
+
timestamp=time.time(),
|
|
931
|
+
cascade=cascade,
|
|
932
|
+
)
|
|
829
933
|
result.updated_cols = [c.qualified_name for c in updated_cols]
|
|
830
934
|
|
|
831
935
|
unmatched_rows = row_update_node.unmatched_rows()
|
|
@@ -833,12 +937,14 @@ class TableVersion:
|
|
|
833
937
|
if error_if_not_exists:
|
|
834
938
|
raise excs.Error(f'batch_update(): {len(unmatched_rows)} row(s) not found')
|
|
835
939
|
if insert_if_not_exists:
|
|
836
|
-
insert_status = self.insert(
|
|
940
|
+
insert_status = self.insert(
|
|
941
|
+
unmatched_rows, None, conn=conn, print_stats=False, fail_on_exception=False
|
|
942
|
+
)
|
|
837
943
|
result += insert_status
|
|
838
944
|
return result
|
|
839
945
|
|
|
840
946
|
def _validate_update_spec(
|
|
841
|
-
|
|
947
|
+
self, value_spec: dict[str, Any], allow_pk: bool, allow_exprs: bool
|
|
842
948
|
) -> dict[Column, exprs.Expr]:
|
|
843
949
|
update_targets: dict[Column, exprs.Expr] = {}
|
|
844
950
|
for col_name, val in value_spec.items():
|
|
@@ -868,7 +974,8 @@ class TableVersion:
|
|
|
868
974
|
if not allow_exprs:
|
|
869
975
|
raise excs.Error(
|
|
870
976
|
f'Column {col_name}: value {val!r} is not a valid literal for this column '
|
|
871
|
-
f'(expected {col.col_type})'
|
|
977
|
+
f'(expected {col.col_type})'
|
|
978
|
+
)
|
|
872
979
|
# it's not a literal, let's try to create an expr from it
|
|
873
980
|
value_expr = exprs.Expr.from_object(val)
|
|
874
981
|
if value_expr is None:
|
|
@@ -883,19 +990,27 @@ class TableVersion:
|
|
|
883
990
|
return update_targets
|
|
884
991
|
|
|
885
992
|
def propagate_update(
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
993
|
+
self,
|
|
994
|
+
plan: Optional[exec.ExecNode],
|
|
995
|
+
where_clause: Optional[sql.ColumnElement],
|
|
996
|
+
recomputed_view_cols: list[Column],
|
|
997
|
+
base_versions: list[Optional[int]],
|
|
998
|
+
conn: sql.engine.Connection,
|
|
999
|
+
timestamp: float,
|
|
1000
|
+
cascade: bool,
|
|
1001
|
+
show_progress: bool = True,
|
|
889
1002
|
) -> UpdateStatus:
|
|
890
1003
|
result = UpdateStatus()
|
|
891
1004
|
if plan is not None:
|
|
892
1005
|
# we're creating a new version
|
|
893
1006
|
self.version += 1
|
|
894
|
-
result.num_rows, result.num_excs, cols_with_excs =
|
|
895
|
-
|
|
1007
|
+
result.num_rows, result.num_excs, cols_with_excs = self.store_tbl.insert_rows(
|
|
1008
|
+
plan, conn, v_min=self.version, show_progress=show_progress
|
|
1009
|
+
)
|
|
896
1010
|
result.cols_with_excs = [f'{self.name}.{self.cols_by_id[cid].name}' for cid in cols_with_excs]
|
|
897
1011
|
self.store_tbl.delete_rows(
|
|
898
|
-
self.version, base_versions=base_versions, match_on_vmin=True, where_clause=where_clause, conn=conn
|
|
1012
|
+
self.version, base_versions=base_versions, match_on_vmin=True, where_clause=where_clause, conn=conn
|
|
1013
|
+
)
|
|
899
1014
|
self._update_md(timestamp, conn)
|
|
900
1015
|
|
|
901
1016
|
if cascade:
|
|
@@ -906,9 +1021,17 @@ class TableVersion:
|
|
|
906
1021
|
plan = None
|
|
907
1022
|
if len(recomputed_cols) > 0:
|
|
908
1023
|
from pixeltable.plan import Planner
|
|
1024
|
+
|
|
909
1025
|
plan = Planner.create_view_update_plan(view.path, recompute_targets=recomputed_cols)
|
|
910
1026
|
status = view.propagate_update(
|
|
911
|
-
plan,
|
|
1027
|
+
plan,
|
|
1028
|
+
None,
|
|
1029
|
+
recomputed_view_cols,
|
|
1030
|
+
base_versions=base_versions,
|
|
1031
|
+
conn=conn,
|
|
1032
|
+
timestamp=timestamp,
|
|
1033
|
+
cascade=True,
|
|
1034
|
+
)
|
|
912
1035
|
result.num_rows += status.num_rows
|
|
913
1036
|
result.num_excs += status.num_excs
|
|
914
1037
|
result.cols_with_excs += status.cols_with_excs
|
|
@@ -924,6 +1047,7 @@ class TableVersion:
|
|
|
924
1047
|
assert self.is_insertable()
|
|
925
1048
|
from pixeltable.exprs import Expr
|
|
926
1049
|
from pixeltable.plan import Planner
|
|
1050
|
+
|
|
927
1051
|
sql_where_clause: Optional[Expr] = None
|
|
928
1052
|
if where is not None:
|
|
929
1053
|
if not isinstance(where, Expr):
|
|
@@ -941,8 +1065,12 @@ class TableVersion:
|
|
|
941
1065
|
return status
|
|
942
1066
|
|
|
943
1067
|
def propagate_delete(
|
|
944
|
-
|
|
945
|
-
|
|
1068
|
+
self,
|
|
1069
|
+
where: Optional[exprs.Expr],
|
|
1070
|
+
base_versions: list[Optional[int]],
|
|
1071
|
+
conn: sql.engine.Connection,
|
|
1072
|
+
timestamp: float,
|
|
1073
|
+
) -> int:
|
|
946
1074
|
"""Delete rows in this table and propagate to views.
|
|
947
1075
|
Args:
|
|
948
1076
|
where: a predicate to filter rows to delete.
|
|
@@ -951,8 +1079,8 @@ class TableVersion:
|
|
|
951
1079
|
"""
|
|
952
1080
|
sql_where_clause = where.sql_expr(exprs.SqlElementCache()) if where is not None else None
|
|
953
1081
|
num_rows = self.store_tbl.delete_rows(
|
|
954
|
-
self.version + 1, base_versions=base_versions, match_on_vmin=False, where_clause=sql_where_clause,
|
|
955
|
-
|
|
1082
|
+
self.version + 1, base_versions=base_versions, match_on_vmin=False, where_clause=sql_where_clause, conn=conn
|
|
1083
|
+
)
|
|
956
1084
|
if num_rows > 0:
|
|
957
1085
|
# we're creating a new version
|
|
958
1086
|
self.version += 1
|
|
@@ -961,12 +1089,12 @@ class TableVersion:
|
|
|
961
1089
|
pass
|
|
962
1090
|
for view in self.mutable_views:
|
|
963
1091
|
num_rows += view.propagate_delete(
|
|
964
|
-
where=None, base_versions=[self.version] + base_versions, conn=conn, timestamp=timestamp
|
|
1092
|
+
where=None, base_versions=[self.version] + base_versions, conn=conn, timestamp=timestamp
|
|
1093
|
+
)
|
|
965
1094
|
return num_rows
|
|
966
1095
|
|
|
967
1096
|
def revert(self) -> None:
|
|
968
|
-
"""Reverts the table to the previous version.
|
|
969
|
-
"""
|
|
1097
|
+
"""Reverts the table to the previous version."""
|
|
970
1098
|
assert not self.is_snapshot
|
|
971
1099
|
if self.version == 0:
|
|
972
1100
|
raise excs.Error('Cannot revert version 0')
|
|
@@ -990,17 +1118,19 @@ class TableVersion:
|
|
|
990
1118
|
# (unclear how to express this with sqlalchemy)
|
|
991
1119
|
query = (
|
|
992
1120
|
f"select ts.dir_id, ts.md->'name' "
|
|
993
|
-
f
|
|
1121
|
+
f'from {schema.Table.__tablename__} ts '
|
|
994
1122
|
f"cross join lateral jsonb_path_query(md, '$.view_md.base_versions[*]') as tbl_version "
|
|
995
1123
|
f"where tbl_version->>0 = '{self.id.hex}' and (tbl_version->>1)::int = {self.version}"
|
|
996
1124
|
)
|
|
997
1125
|
result = list(conn.execute(sql.text(query)))
|
|
998
1126
|
if len(result) > 0:
|
|
999
1127
|
names = [row[1] for row in result]
|
|
1000
|
-
raise excs.Error(
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1128
|
+
raise excs.Error(
|
|
1129
|
+
(
|
|
1130
|
+
f'Current version is needed for {len(result)} snapshot{"s" if len(result) > 1 else ""} '
|
|
1131
|
+
f'({", ".join(names)})'
|
|
1132
|
+
)
|
|
1133
|
+
)
|
|
1004
1134
|
|
|
1005
1135
|
conn = session.connection()
|
|
1006
1136
|
# delete newly-added data
|
|
@@ -1013,9 +1143,7 @@ class TableVersion:
|
|
|
1013
1143
|
# copy the index value back from the undo column and reset the undo column to NULL
|
|
1014
1144
|
set_clause[index_info.val_col.sa_col] = index_info.undo_col.sa_col
|
|
1015
1145
|
set_clause[index_info.undo_col.sa_col] = None
|
|
1016
|
-
stmt = sql.update(self.store_tbl.sa_tbl)
|
|
1017
|
-
.values(set_clause) \
|
|
1018
|
-
.where(self.store_tbl.sa_tbl.c.v_max == self.version)
|
|
1146
|
+
stmt = sql.update(self.store_tbl.sa_tbl).values(set_clause).where(self.store_tbl.sa_tbl.c.v_max == self.version)
|
|
1019
1147
|
conn.execute(stmt)
|
|
1020
1148
|
|
|
1021
1149
|
# revert schema changes
|
|
@@ -1049,38 +1177,45 @@ class TableVersion:
|
|
|
1049
1177
|
md.schema_version_drop = None
|
|
1050
1178
|
|
|
1051
1179
|
# we need to determine the preceding schema version and reload the schema
|
|
1052
|
-
schema_version_md_dict =
|
|
1053
|
-
.
|
|
1054
|
-
.where(schema.TableSchemaVersion.
|
|
1180
|
+
schema_version_md_dict = (
|
|
1181
|
+
session.query(schema.TableSchemaVersion.md)
|
|
1182
|
+
.where(schema.TableSchemaVersion.tbl_id == self.id)
|
|
1183
|
+
.where(schema.TableSchemaVersion.schema_version == self.schema_version)
|
|
1055
1184
|
.scalar()
|
|
1185
|
+
)
|
|
1056
1186
|
preceding_schema_version = schema_version_md_dict['preceding_schema_version']
|
|
1057
|
-
preceding_schema_version_md_dict =
|
|
1058
|
-
.
|
|
1059
|
-
.where(schema.TableSchemaVersion.
|
|
1187
|
+
preceding_schema_version_md_dict = (
|
|
1188
|
+
session.query(schema.TableSchemaVersion.md)
|
|
1189
|
+
.where(schema.TableSchemaVersion.tbl_id == self.id)
|
|
1190
|
+
.where(schema.TableSchemaVersion.schema_version == preceding_schema_version)
|
|
1060
1191
|
.scalar()
|
|
1192
|
+
)
|
|
1061
1193
|
preceding_schema_version_md = schema.md_from_dict(
|
|
1062
|
-
schema.TableSchemaVersionMd, preceding_schema_version_md_dict
|
|
1194
|
+
schema.TableSchemaVersionMd, preceding_schema_version_md_dict
|
|
1195
|
+
)
|
|
1063
1196
|
tbl_md = self._create_tbl_md()
|
|
1064
1197
|
self._init_schema(tbl_md, preceding_schema_version_md)
|
|
1065
1198
|
|
|
1066
1199
|
conn.execute(
|
|
1067
1200
|
sql.delete(schema.TableSchemaVersion.__table__)
|
|
1068
|
-
|
|
1069
|
-
|
|
1201
|
+
.where(schema.TableSchemaVersion.tbl_id == self.id)
|
|
1202
|
+
.where(schema.TableSchemaVersion.schema_version == self.schema_version)
|
|
1203
|
+
)
|
|
1070
1204
|
self.schema_version = preceding_schema_version
|
|
1071
1205
|
self.comment = preceding_schema_version_md.comment
|
|
1072
1206
|
self.num_retained_versions = preceding_schema_version_md.num_retained_versions
|
|
1073
1207
|
|
|
1074
1208
|
conn.execute(
|
|
1075
1209
|
sql.delete(schema.TableVersion.__table__)
|
|
1076
|
-
|
|
1077
|
-
|
|
1210
|
+
.where(schema.TableVersion.tbl_id == self.id)
|
|
1211
|
+
.where(schema.TableVersion.version == self.version)
|
|
1078
1212
|
)
|
|
1079
1213
|
self.version -= 1
|
|
1080
1214
|
conn.execute(
|
|
1081
1215
|
sql.update(schema.Table.__table__)
|
|
1082
|
-
|
|
1083
|
-
|
|
1216
|
+
.values({schema.Table.md: dataclasses.asdict(self._create_tbl_md())})
|
|
1217
|
+
.where(schema.Table.id == self.id)
|
|
1218
|
+
)
|
|
1084
1219
|
|
|
1085
1220
|
# propagate to views
|
|
1086
1221
|
for view in self.mutable_views:
|
|
@@ -1152,9 +1287,9 @@ class TableVersion:
|
|
|
1152
1287
|
return names
|
|
1153
1288
|
|
|
1154
1289
|
def _record_refd_columns(self, col: Column) -> None:
|
|
1155
|
-
"""Update Column.dependent_cols for all cols referenced in col.value_expr.
|
|
1156
|
-
"""
|
|
1290
|
+
"""Update Column.dependent_cols for all cols referenced in col.value_expr."""
|
|
1157
1291
|
import pixeltable.exprs as exprs
|
|
1292
|
+
|
|
1158
1293
|
if col.value_expr_dict is not None:
|
|
1159
1294
|
# if we have a value_expr_dict, use that instead of instantiating the value_expr
|
|
1160
1295
|
refd_cols = exprs.Expr.get_refd_columns(col.value_expr_dict)
|
|
@@ -1188,27 +1323,34 @@ class TableVersion:
|
|
|
1188
1323
|
for col in cols:
|
|
1189
1324
|
value_expr_dict = col.value_expr.as_dict() if col.value_expr is not None else None
|
|
1190
1325
|
column_md[col.id] = schema.ColumnMd(
|
|
1191
|
-
id=col.id,
|
|
1192
|
-
|
|
1193
|
-
|
|
1326
|
+
id=col.id,
|
|
1327
|
+
col_type=col.col_type.as_dict(),
|
|
1328
|
+
is_pk=col.is_pk,
|
|
1329
|
+
schema_version_add=col.schema_version_add,
|
|
1330
|
+
schema_version_drop=col.schema_version_drop,
|
|
1331
|
+
value_expr=value_expr_dict,
|
|
1332
|
+
stored=col.stored,
|
|
1333
|
+
)
|
|
1194
1334
|
return column_md
|
|
1195
1335
|
|
|
1196
1336
|
@classmethod
|
|
1197
1337
|
def _create_stores_md(cls, stores: Iterable[pxt.io.ExternalStore]) -> list[dict[str, Any]]:
|
|
1198
1338
|
return [
|
|
1199
|
-
{
|
|
1200
|
-
'class': f'{type(store).__module__}.{type(store).__qualname__}',
|
|
1201
|
-
'md': store.as_dict()
|
|
1202
|
-
}
|
|
1203
|
-
for store in stores
|
|
1339
|
+
{'class': f'{type(store).__module__}.{type(store).__qualname__}', 'md': store.as_dict()} for store in stores
|
|
1204
1340
|
]
|
|
1205
1341
|
|
|
1206
1342
|
def _create_tbl_md(self) -> schema.TableMd:
|
|
1207
1343
|
return schema.TableMd(
|
|
1208
|
-
name=self.name,
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1344
|
+
name=self.name,
|
|
1345
|
+
current_version=self.version,
|
|
1346
|
+
current_schema_version=self.schema_version,
|
|
1347
|
+
next_col_id=self.next_col_id,
|
|
1348
|
+
next_idx_id=self.next_idx_id,
|
|
1349
|
+
next_row_id=self.next_rowid,
|
|
1350
|
+
column_md=self._create_column_md(self.cols),
|
|
1351
|
+
index_md=self.idx_md,
|
|
1352
|
+
external_stores=self._create_stores_md(self.external_stores.values()),
|
|
1353
|
+
view_md=self.view_md,
|
|
1212
1354
|
)
|
|
1213
1355
|
|
|
1214
1356
|
def _create_version_md(self, timestamp: float) -> schema.TableVersionMd:
|
|
@@ -1218,13 +1360,19 @@ class TableVersion:
|
|
|
1218
1360
|
column_md: dict[int, schema.SchemaColumn] = {}
|
|
1219
1361
|
for pos, col in enumerate(self.cols_by_name.values()):
|
|
1220
1362
|
column_md[col.id] = schema.SchemaColumn(
|
|
1221
|
-
pos=pos,
|
|
1222
|
-
|
|
1363
|
+
pos=pos,
|
|
1364
|
+
name=col.name,
|
|
1365
|
+
media_validation=col._media_validation.name.lower() if col._media_validation is not None else None,
|
|
1366
|
+
)
|
|
1223
1367
|
# preceding_schema_version to be set by the caller
|
|
1224
1368
|
return schema.TableSchemaVersionMd(
|
|
1225
|
-
schema_version=self.schema_version,
|
|
1226
|
-
|
|
1227
|
-
|
|
1369
|
+
schema_version=self.schema_version,
|
|
1370
|
+
preceding_schema_version=preceding_schema_version,
|
|
1371
|
+
columns=column_md,
|
|
1372
|
+
num_retained_versions=self.num_retained_versions,
|
|
1373
|
+
comment=self.comment,
|
|
1374
|
+
media_validation=self.media_validation.name.lower(),
|
|
1375
|
+
)
|
|
1228
1376
|
|
|
1229
1377
|
def as_dict(self) -> dict:
|
|
1230
1378
|
return {'id': str(self.id), 'effective_version': self.effective_version}
|
|
@@ -1232,6 +1380,7 @@ class TableVersion:
|
|
|
1232
1380
|
@classmethod
|
|
1233
1381
|
def from_dict(cls, d: dict) -> TableVersion:
|
|
1234
1382
|
import pixeltable.catalog as catalog
|
|
1383
|
+
|
|
1235
1384
|
id = UUID(d['id'])
|
|
1236
1385
|
effective_version = d['effective_version']
|
|
1237
1386
|
return catalog.Catalog.get().tbl_versions[(id, effective_version)]
|