pixeltable 0.2.26__py3-none-any.whl → 0.5.7__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.
- pixeltable/__init__.py +83 -19
- pixeltable/_query.py +1444 -0
- pixeltable/_version.py +1 -0
- pixeltable/catalog/__init__.py +7 -4
- pixeltable/catalog/catalog.py +2394 -119
- pixeltable/catalog/column.py +225 -104
- pixeltable/catalog/dir.py +38 -9
- pixeltable/catalog/globals.py +53 -34
- pixeltable/catalog/insertable_table.py +265 -115
- pixeltable/catalog/path.py +80 -17
- pixeltable/catalog/schema_object.py +28 -43
- pixeltable/catalog/table.py +1270 -677
- pixeltable/catalog/table_metadata.py +103 -0
- pixeltable/catalog/table_version.py +1270 -751
- pixeltable/catalog/table_version_handle.py +109 -0
- pixeltable/catalog/table_version_path.py +137 -42
- pixeltable/catalog/tbl_ops.py +53 -0
- pixeltable/catalog/update_status.py +191 -0
- pixeltable/catalog/view.py +251 -134
- pixeltable/config.py +215 -0
- pixeltable/env.py +736 -285
- pixeltable/exceptions.py +26 -2
- pixeltable/exec/__init__.py +7 -2
- pixeltable/exec/aggregation_node.py +39 -21
- pixeltable/exec/cache_prefetch_node.py +87 -109
- pixeltable/exec/cell_materialization_node.py +268 -0
- pixeltable/exec/cell_reconstruction_node.py +168 -0
- pixeltable/exec/component_iteration_node.py +25 -28
- pixeltable/exec/data_row_batch.py +11 -46
- pixeltable/exec/exec_context.py +26 -11
- pixeltable/exec/exec_node.py +35 -27
- pixeltable/exec/expr_eval/__init__.py +3 -0
- pixeltable/exec/expr_eval/evaluators.py +365 -0
- pixeltable/exec/expr_eval/expr_eval_node.py +413 -0
- pixeltable/exec/expr_eval/globals.py +200 -0
- pixeltable/exec/expr_eval/row_buffer.py +74 -0
- pixeltable/exec/expr_eval/schedulers.py +413 -0
- pixeltable/exec/globals.py +35 -0
- pixeltable/exec/in_memory_data_node.py +35 -27
- pixeltable/exec/object_store_save_node.py +293 -0
- pixeltable/exec/row_update_node.py +44 -29
- pixeltable/exec/sql_node.py +414 -115
- pixeltable/exprs/__init__.py +8 -5
- pixeltable/exprs/arithmetic_expr.py +79 -45
- pixeltable/exprs/array_slice.py +5 -5
- pixeltable/exprs/column_property_ref.py +40 -26
- pixeltable/exprs/column_ref.py +254 -61
- pixeltable/exprs/comparison.py +14 -9
- pixeltable/exprs/compound_predicate.py +9 -10
- pixeltable/exprs/data_row.py +213 -72
- pixeltable/exprs/expr.py +270 -104
- pixeltable/exprs/expr_dict.py +6 -5
- pixeltable/exprs/expr_set.py +20 -11
- pixeltable/exprs/function_call.py +383 -284
- pixeltable/exprs/globals.py +18 -5
- pixeltable/exprs/in_predicate.py +7 -7
- pixeltable/exprs/inline_expr.py +37 -37
- pixeltable/exprs/is_null.py +8 -4
- pixeltable/exprs/json_mapper.py +120 -54
- pixeltable/exprs/json_path.py +90 -60
- pixeltable/exprs/literal.py +61 -16
- pixeltable/exprs/method_ref.py +7 -6
- pixeltable/exprs/object_ref.py +19 -8
- pixeltable/exprs/row_builder.py +238 -75
- pixeltable/exprs/rowid_ref.py +53 -15
- pixeltable/exprs/similarity_expr.py +65 -50
- pixeltable/exprs/sql_element_cache.py +5 -5
- pixeltable/exprs/string_op.py +107 -0
- pixeltable/exprs/type_cast.py +25 -13
- pixeltable/exprs/variable.py +2 -2
- pixeltable/func/__init__.py +9 -5
- pixeltable/func/aggregate_function.py +197 -92
- pixeltable/func/callable_function.py +119 -35
- pixeltable/func/expr_template_function.py +101 -48
- pixeltable/func/function.py +375 -62
- pixeltable/func/function_registry.py +20 -19
- pixeltable/func/globals.py +6 -5
- pixeltable/func/mcp.py +74 -0
- pixeltable/func/query_template_function.py +151 -35
- pixeltable/func/signature.py +178 -49
- pixeltable/func/tools.py +164 -0
- pixeltable/func/udf.py +176 -53
- pixeltable/functions/__init__.py +44 -4
- pixeltable/functions/anthropic.py +226 -47
- pixeltable/functions/audio.py +148 -11
- pixeltable/functions/bedrock.py +137 -0
- pixeltable/functions/date.py +188 -0
- pixeltable/functions/deepseek.py +113 -0
- pixeltable/functions/document.py +81 -0
- pixeltable/functions/fal.py +76 -0
- pixeltable/functions/fireworks.py +72 -20
- pixeltable/functions/gemini.py +249 -0
- pixeltable/functions/globals.py +208 -53
- pixeltable/functions/groq.py +108 -0
- pixeltable/functions/huggingface.py +1088 -95
- pixeltable/functions/image.py +155 -84
- pixeltable/functions/json.py +8 -11
- pixeltable/functions/llama_cpp.py +31 -19
- pixeltable/functions/math.py +169 -0
- pixeltable/functions/mistralai.py +50 -75
- pixeltable/functions/net.py +70 -0
- pixeltable/functions/ollama.py +29 -36
- pixeltable/functions/openai.py +548 -160
- pixeltable/functions/openrouter.py +143 -0
- pixeltable/functions/replicate.py +15 -14
- pixeltable/functions/reve.py +250 -0
- pixeltable/functions/string.py +310 -85
- pixeltable/functions/timestamp.py +37 -19
- pixeltable/functions/together.py +77 -120
- pixeltable/functions/twelvelabs.py +188 -0
- pixeltable/functions/util.py +7 -2
- pixeltable/functions/uuid.py +30 -0
- pixeltable/functions/video.py +1528 -117
- pixeltable/functions/vision.py +26 -26
- pixeltable/functions/voyageai.py +289 -0
- pixeltable/functions/whisper.py +19 -10
- pixeltable/functions/whisperx.py +179 -0
- pixeltable/functions/yolox.py +112 -0
- pixeltable/globals.py +716 -236
- pixeltable/index/__init__.py +3 -1
- pixeltable/index/base.py +17 -21
- pixeltable/index/btree.py +32 -22
- pixeltable/index/embedding_index.py +155 -92
- pixeltable/io/__init__.py +12 -7
- pixeltable/io/datarows.py +140 -0
- pixeltable/io/external_store.py +83 -125
- pixeltable/io/fiftyone.py +24 -33
- pixeltable/io/globals.py +47 -182
- pixeltable/io/hf_datasets.py +96 -127
- pixeltable/io/label_studio.py +171 -156
- pixeltable/io/lancedb.py +3 -0
- pixeltable/io/pandas.py +136 -115
- pixeltable/io/parquet.py +40 -153
- pixeltable/io/table_data_conduit.py +702 -0
- pixeltable/io/utils.py +100 -0
- pixeltable/iterators/__init__.py +8 -4
- pixeltable/iterators/audio.py +207 -0
- pixeltable/iterators/base.py +9 -3
- pixeltable/iterators/document.py +144 -87
- pixeltable/iterators/image.py +17 -38
- pixeltable/iterators/string.py +15 -12
- pixeltable/iterators/video.py +523 -127
- pixeltable/metadata/__init__.py +33 -8
- pixeltable/metadata/converters/convert_10.py +2 -3
- pixeltable/metadata/converters/convert_13.py +2 -2
- pixeltable/metadata/converters/convert_15.py +15 -11
- pixeltable/metadata/converters/convert_16.py +4 -5
- pixeltable/metadata/converters/convert_17.py +4 -5
- pixeltable/metadata/converters/convert_18.py +4 -6
- pixeltable/metadata/converters/convert_19.py +6 -9
- pixeltable/metadata/converters/convert_20.py +3 -6
- pixeltable/metadata/converters/convert_21.py +6 -8
- pixeltable/metadata/converters/convert_22.py +3 -2
- pixeltable/metadata/converters/convert_23.py +33 -0
- pixeltable/metadata/converters/convert_24.py +55 -0
- pixeltable/metadata/converters/convert_25.py +19 -0
- pixeltable/metadata/converters/convert_26.py +23 -0
- pixeltable/metadata/converters/convert_27.py +29 -0
- pixeltable/metadata/converters/convert_28.py +13 -0
- pixeltable/metadata/converters/convert_29.py +110 -0
- pixeltable/metadata/converters/convert_30.py +63 -0
- pixeltable/metadata/converters/convert_31.py +11 -0
- pixeltable/metadata/converters/convert_32.py +15 -0
- pixeltable/metadata/converters/convert_33.py +17 -0
- pixeltable/metadata/converters/convert_34.py +21 -0
- pixeltable/metadata/converters/convert_35.py +9 -0
- pixeltable/metadata/converters/convert_36.py +38 -0
- pixeltable/metadata/converters/convert_37.py +15 -0
- pixeltable/metadata/converters/convert_38.py +39 -0
- pixeltable/metadata/converters/convert_39.py +124 -0
- pixeltable/metadata/converters/convert_40.py +73 -0
- pixeltable/metadata/converters/convert_41.py +12 -0
- pixeltable/metadata/converters/convert_42.py +9 -0
- pixeltable/metadata/converters/convert_43.py +44 -0
- pixeltable/metadata/converters/util.py +44 -18
- pixeltable/metadata/notes.py +21 -0
- pixeltable/metadata/schema.py +185 -42
- pixeltable/metadata/utils.py +74 -0
- pixeltable/mypy/__init__.py +3 -0
- pixeltable/mypy/mypy_plugin.py +123 -0
- pixeltable/plan.py +616 -225
- pixeltable/share/__init__.py +3 -0
- pixeltable/share/packager.py +797 -0
- pixeltable/share/protocol/__init__.py +33 -0
- pixeltable/share/protocol/common.py +165 -0
- pixeltable/share/protocol/operation_types.py +33 -0
- pixeltable/share/protocol/replica.py +119 -0
- pixeltable/share/publish.py +349 -0
- pixeltable/store.py +398 -232
- pixeltable/type_system.py +730 -267
- pixeltable/utils/__init__.py +40 -0
- pixeltable/utils/arrow.py +201 -29
- pixeltable/utils/av.py +298 -0
- pixeltable/utils/azure_store.py +346 -0
- pixeltable/utils/coco.py +26 -27
- pixeltable/utils/code.py +4 -4
- pixeltable/utils/console_output.py +46 -0
- pixeltable/utils/coroutine.py +24 -0
- pixeltable/utils/dbms.py +92 -0
- pixeltable/utils/description_helper.py +11 -12
- pixeltable/utils/documents.py +60 -61
- pixeltable/utils/exception_handler.py +36 -0
- pixeltable/utils/filecache.py +38 -22
- pixeltable/utils/formatter.py +88 -51
- pixeltable/utils/gcs_store.py +295 -0
- pixeltable/utils/http.py +133 -0
- pixeltable/utils/http_server.py +14 -13
- pixeltable/utils/iceberg.py +13 -0
- pixeltable/utils/image.py +17 -0
- pixeltable/utils/lancedb.py +90 -0
- pixeltable/utils/local_store.py +322 -0
- pixeltable/utils/misc.py +5 -0
- pixeltable/utils/object_stores.py +573 -0
- pixeltable/utils/pydantic.py +60 -0
- pixeltable/utils/pytorch.py +20 -20
- pixeltable/utils/s3_store.py +527 -0
- pixeltable/utils/sql.py +32 -5
- pixeltable/utils/system.py +30 -0
- pixeltable/utils/transactional_directory.py +4 -3
- pixeltable-0.5.7.dist-info/METADATA +579 -0
- pixeltable-0.5.7.dist-info/RECORD +227 -0
- {pixeltable-0.2.26.dist-info → pixeltable-0.5.7.dist-info}/WHEEL +1 -1
- pixeltable-0.5.7.dist-info/entry_points.txt +2 -0
- pixeltable/__version__.py +0 -3
- pixeltable/catalog/named_function.py +0 -36
- pixeltable/catalog/path_dict.py +0 -141
- pixeltable/dataframe.py +0 -894
- pixeltable/exec/expr_eval_node.py +0 -232
- pixeltable/ext/__init__.py +0 -14
- pixeltable/ext/functions/__init__.py +0 -8
- pixeltable/ext/functions/whisperx.py +0 -77
- pixeltable/ext/functions/yolox.py +0 -157
- pixeltable/tool/create_test_db_dump.py +0 -311
- pixeltable/tool/create_test_video.py +0 -81
- pixeltable/tool/doc_plugins/griffe.py +0 -50
- pixeltable/tool/doc_plugins/mkdocstrings.py +0 -6
- pixeltable/tool/doc_plugins/templates/material/udf.html.jinja +0 -135
- pixeltable/tool/embed_udf.py +0 -9
- pixeltable/tool/mypy_plugin.py +0 -55
- pixeltable/utils/media_store.py +0 -76
- pixeltable/utils/s3.py +0 -16
- pixeltable-0.2.26.dist-info/METADATA +0 -400
- pixeltable-0.2.26.dist-info/RECORD +0 -156
- pixeltable-0.2.26.dist-info/entry_points.txt +0 -3
- {pixeltable-0.2.26.dist-info → pixeltable-0.5.7.dist-info/licenses}/LICENSE +0 -0
pixeltable/exprs/__init__.py
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
|
+
# ruff: noqa: F401
|
|
2
|
+
|
|
1
3
|
from .arithmetic_expr import ArithmeticExpr
|
|
2
4
|
from .array_slice import ArraySlice
|
|
3
5
|
from .column_property_ref import ColumnPropertyRef
|
|
4
6
|
from .column_ref import ColumnRef
|
|
5
7
|
from .comparison import Comparison
|
|
6
8
|
from .compound_predicate import CompoundPredicate
|
|
7
|
-
from .data_row import DataRow
|
|
9
|
+
from .data_row import ArrayMd, BinaryMd, CellMd, DataRow
|
|
8
10
|
from .expr import Expr
|
|
9
11
|
from .expr_dict import ExprDict
|
|
10
12
|
from .expr_set import ExprSet
|
|
11
13
|
from .function_call import FunctionCall
|
|
14
|
+
from .globals import ArithmeticOperator, ComparisonOperator, LogicalOperator
|
|
12
15
|
from .in_predicate import InPredicate
|
|
13
16
|
from .inline_expr import InlineArray, InlineDict, InlineList
|
|
14
17
|
from .is_null import IsNull
|
|
15
|
-
from .json_mapper import JsonMapper
|
|
16
|
-
from .json_path import
|
|
18
|
+
from .json_mapper import JsonMapper, JsonMapperDispatch
|
|
19
|
+
from .json_path import JsonPath
|
|
17
20
|
from .literal import Literal
|
|
18
21
|
from .method_ref import MethodRef
|
|
19
22
|
from .object_ref import ObjectRef
|
|
20
|
-
from .row_builder import
|
|
23
|
+
from .row_builder import ColumnSlotIdx, ExecProfile, RowBuilder
|
|
21
24
|
from .rowid_ref import RowidRef
|
|
22
25
|
from .similarity_expr import SimilarityExpr
|
|
23
26
|
from .sql_element_cache import SqlElementCache
|
|
27
|
+
from .string_op import StringOp
|
|
24
28
|
from .type_cast import TypeCast
|
|
25
29
|
from .variable import Variable
|
|
26
|
-
from .globals import ComparisonOperator, LogicalOperator, ArithmeticOperator
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
import sqlalchemy as sql
|
|
6
6
|
|
|
7
|
-
import
|
|
8
|
-
import pixeltable.type_system as ts
|
|
7
|
+
from pixeltable import env, exceptions as excs, type_system as ts
|
|
9
8
|
|
|
10
9
|
from .data_row import DataRow
|
|
11
10
|
from .expr import Expr
|
|
12
11
|
from .globals import ArithmeticOperator
|
|
12
|
+
from .literal import Literal
|
|
13
13
|
from .row_builder import RowBuilder
|
|
14
14
|
from .sql_element_cache import SqlElementCache
|
|
15
15
|
|
|
@@ -18,6 +18,9 @@ class ArithmeticExpr(Expr):
|
|
|
18
18
|
"""
|
|
19
19
|
Allows arithmetic exprs on json paths
|
|
20
20
|
"""
|
|
21
|
+
|
|
22
|
+
operator: ArithmeticOperator
|
|
23
|
+
|
|
21
24
|
def __init__(self, operator: ArithmeticOperator, op1: Expr, op2: Expr):
|
|
22
25
|
if op1.col_type.is_json_type() or op2.col_type.is_json_type() or operator == ArithmeticOperator.DIV:
|
|
23
26
|
# we assume it's a float
|
|
@@ -35,49 +38,56 @@ class ArithmeticExpr(Expr):
|
|
|
35
38
|
|
|
36
39
|
self.id = self._create_id()
|
|
37
40
|
|
|
41
|
+
@property
|
|
42
|
+
def _op1(self) -> Expr:
|
|
43
|
+
return self.components[0]
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def _op2(self) -> Expr:
|
|
47
|
+
return self.components[1]
|
|
48
|
+
|
|
38
49
|
def __repr__(self) -> str:
|
|
39
50
|
# add parentheses around operands that are ArithmeticExprs to express precedence
|
|
40
51
|
op1_str = f'({self._op1})' if isinstance(self._op1, ArithmeticExpr) else str(self._op1)
|
|
41
52
|
op2_str = f'({self._op2})' if isinstance(self._op2, ArithmeticExpr) else str(self._op2)
|
|
42
|
-
return f'{op1_str} {
|
|
53
|
+
return f'{op1_str} {self.operator} {op2_str}'
|
|
43
54
|
|
|
44
55
|
def _equals(self, other: ArithmeticExpr) -> bool:
|
|
45
56
|
return self.operator == other.operator
|
|
46
57
|
|
|
47
58
|
def _id_attrs(self) -> list[tuple[str, Any]]:
|
|
48
|
-
return super()._id_attrs()
|
|
49
|
-
|
|
50
|
-
@property
|
|
51
|
-
def _op1(self) -> Expr:
|
|
52
|
-
return self.components[0]
|
|
59
|
+
return [*super()._id_attrs(), ('operator', self.operator.value)]
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
def _op2(self) -> Expr:
|
|
56
|
-
return self.components[1]
|
|
57
|
-
|
|
58
|
-
def sql_expr(self, sql_elements: SqlElementCache) -> Optional[sql.ColumnElement]:
|
|
61
|
+
def sql_expr(self, sql_elements: SqlElementCache) -> sql.ColumnElement | None:
|
|
59
62
|
assert self.col_type.is_int_type() or self.col_type.is_float_type() or self.col_type.is_json_type()
|
|
60
63
|
left = sql_elements.get(self._op1)
|
|
61
64
|
right = sql_elements.get(self._op2)
|
|
62
65
|
if left is None or right is None:
|
|
63
66
|
return None
|
|
64
|
-
if self.operator
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
if self.operator in (ArithmeticOperator.ADD, ArithmeticOperator.SUB, ArithmeticOperator.MUL):
|
|
68
|
+
if env.Env.get().is_using_cockroachdb and self._op1.col_type != self._op2.col_type:
|
|
69
|
+
if self._op1.col_type != self.col_type:
|
|
70
|
+
left = sql.cast(left, self.col_type.to_sa_type())
|
|
71
|
+
if self._op2.col_type != self.col_type:
|
|
72
|
+
right = sql.cast(right, self.col_type.to_sa_type())
|
|
73
|
+
if self.operator == ArithmeticOperator.ADD:
|
|
74
|
+
return left + right
|
|
75
|
+
if self.operator == ArithmeticOperator.SUB:
|
|
76
|
+
return left - right
|
|
77
|
+
if self.operator == ArithmeticOperator.MUL:
|
|
78
|
+
return left * right
|
|
70
79
|
if self.operator == ArithmeticOperator.DIV:
|
|
71
80
|
assert self.col_type.is_float_type()
|
|
72
|
-
# Avoid
|
|
81
|
+
# Avoid division by zero errors by converting any zero divisor to NULL.
|
|
73
82
|
# TODO: Should we cast the NULLs to NaNs when they are retrieved back into Python?
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return sql.
|
|
83
|
+
# These casts cause the computation to take place in float units, rather than DECIMAL.
|
|
84
|
+
nullif = sql.cast(sql.func.nullif(right, 0), self.col_type.to_sa_type())
|
|
85
|
+
return sql.cast(left, self.col_type.to_sa_type()) / nullif
|
|
77
86
|
if self.operator == ArithmeticOperator.MOD:
|
|
78
87
|
if self.col_type.is_int_type():
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
# Avoid division by zero errors by converting any zero divisor to NULL.
|
|
89
|
+
nullif1 = sql.cast(sql.func.nullif(right, 0), self.col_type.to_sa_type())
|
|
90
|
+
return left % nullif1
|
|
81
91
|
if self.col_type.is_float_type():
|
|
82
92
|
# Postgres does not support modulus for floats
|
|
83
93
|
return None
|
|
@@ -87,42 +97,66 @@ class ArithmeticExpr(Expr):
|
|
|
87
97
|
# We need the behavior to be consistent, so that expressions will evaluate the same way
|
|
88
98
|
# whether or not their operands can be translated to SQL. These SQL clauses should
|
|
89
99
|
# mimic the behavior of Python's // operator.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return sql.sql.expression.cast(sql.func.floor(left / nullif), sql.Float)
|
|
95
|
-
assert False
|
|
100
|
+
# Avoid division by zero errors by converting any zero divisor to NULL.
|
|
101
|
+
nullif = sql.cast(sql.func.nullif(right, 0), self.col_type.to_sa_type())
|
|
102
|
+
return sql.func.floor(sql.cast(left, self.col_type.to_sa_type()) / nullif)
|
|
103
|
+
raise AssertionError()
|
|
96
104
|
|
|
97
105
|
def eval(self, data_row: DataRow, row_builder: RowBuilder) -> None:
|
|
98
106
|
op1_val = data_row[self._op1.slot_idx]
|
|
99
107
|
op2_val = data_row[self._op2.slot_idx]
|
|
100
108
|
|
|
101
109
|
# if one or both columns is JsonTyped, we need a dynamic check that they are numeric
|
|
102
|
-
if self._op1.col_type.is_json_type() and not
|
|
110
|
+
if self._op1.col_type.is_json_type() and op1_val is not None and not isinstance(op1_val, (int, float)):
|
|
103
111
|
raise excs.Error(
|
|
104
|
-
f'{self.operator} requires numeric
|
|
105
|
-
|
|
112
|
+
f'{self.operator} requires numeric types, but {self._op1} has type {type(op1_val).__name__}'
|
|
113
|
+
)
|
|
114
|
+
if self._op2.col_type.is_json_type() and op2_val is not None and not isinstance(op2_val, (int, float)):
|
|
106
115
|
raise excs.Error(
|
|
107
|
-
f'{self.operator} requires numeric
|
|
116
|
+
f'{self.operator} requires numeric types, but {self._op2} has type {type(op2_val).__name__}'
|
|
117
|
+
)
|
|
108
118
|
|
|
109
|
-
|
|
119
|
+
data_row[self.slot_idx] = self.eval_nullable(op1_val, op2_val)
|
|
120
|
+
|
|
121
|
+
def eval_nullable(self, op1_val: float | None, op2_val: float | None) -> float | None:
|
|
122
|
+
"""
|
|
123
|
+
Return the result of evaluating the expression on two nullable int/float operands,
|
|
124
|
+
None is interpreted as SQL NULL
|
|
125
|
+
"""
|
|
110
126
|
if op1_val is None or op2_val is None:
|
|
111
|
-
|
|
112
|
-
|
|
127
|
+
return None
|
|
128
|
+
return self.eval_non_null(op1_val, op2_val)
|
|
113
129
|
|
|
130
|
+
def eval_non_null(self, op1_val: float, op2_val: float) -> float:
|
|
131
|
+
"""
|
|
132
|
+
Return the result of evaluating the expression on two int/float operands
|
|
133
|
+
"""
|
|
114
134
|
if self.operator == ArithmeticOperator.ADD:
|
|
115
|
-
|
|
135
|
+
return op1_val + op2_val
|
|
116
136
|
elif self.operator == ArithmeticOperator.SUB:
|
|
117
|
-
|
|
137
|
+
return op1_val - op2_val
|
|
118
138
|
elif self.operator == ArithmeticOperator.MUL:
|
|
119
|
-
|
|
139
|
+
return op1_val * op2_val
|
|
120
140
|
elif self.operator == ArithmeticOperator.DIV:
|
|
121
|
-
|
|
141
|
+
return op1_val / op2_val
|
|
122
142
|
elif self.operator == ArithmeticOperator.MOD:
|
|
123
|
-
|
|
143
|
+
return op1_val % op2_val
|
|
124
144
|
elif self.operator == ArithmeticOperator.FLOORDIV:
|
|
125
|
-
|
|
145
|
+
return op1_val // op2_val
|
|
146
|
+
|
|
147
|
+
def as_literal(self) -> Literal | None:
|
|
148
|
+
op1_lit = self._op1.as_literal()
|
|
149
|
+
if op1_lit is None:
|
|
150
|
+
return None
|
|
151
|
+
op2_lit = self._op2.as_literal()
|
|
152
|
+
if op2_lit is None:
|
|
153
|
+
return None
|
|
154
|
+
op1_val = op1_lit.val
|
|
155
|
+
assert op1_lit.col_type.is_numeric_type() or op1_val is None
|
|
156
|
+
op2_val = op2_lit.val
|
|
157
|
+
assert op2_lit.col_type.is_numeric_type() or op2_val is None
|
|
158
|
+
|
|
159
|
+
return Literal(self.eval_nullable(op1_val, op2_val), self.col_type) # type: ignore[arg-type]
|
|
126
160
|
|
|
127
161
|
def _as_dict(self) -> dict:
|
|
128
162
|
return {'operator': self.operator.value, **super()._as_dict()}
|
pixeltable/exprs/array_slice.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
import sqlalchemy as sql
|
|
6
6
|
|
|
@@ -15,7 +15,8 @@ class ArraySlice(Expr):
|
|
|
15
15
|
"""
|
|
16
16
|
Slice operation on an array, eg, t.array_col[:, 1:2].
|
|
17
17
|
"""
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
def __init__(self, arr: Expr, index: tuple[int | slice, ...]):
|
|
19
20
|
assert arr.col_type.is_array_type()
|
|
20
21
|
# determine result type
|
|
21
22
|
super().__init__(arr.col_type)
|
|
@@ -40,9 +41,9 @@ class ArraySlice(Expr):
|
|
|
40
41
|
return self.index == other.index
|
|
41
42
|
|
|
42
43
|
def _id_attrs(self) -> list[tuple[str, Any]]:
|
|
43
|
-
return super()._id_attrs()
|
|
44
|
+
return [*super()._id_attrs(), ('index', self.index)]
|
|
44
45
|
|
|
45
|
-
def sql_expr(self, _: SqlElementCache) ->
|
|
46
|
+
def sql_expr(self, _: SqlElementCache) -> sql.ColumnElement | None:
|
|
46
47
|
return None
|
|
47
48
|
|
|
48
49
|
def eval(self, data_row: DataRow, row_builder: RowBuilder) -> None:
|
|
@@ -68,4 +69,3 @@ class ArraySlice(Expr):
|
|
|
68
69
|
else:
|
|
69
70
|
index.append(el)
|
|
70
71
|
return cls(components[0], tuple(index))
|
|
71
|
-
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import enum
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
import sqlalchemy as sql
|
|
7
7
|
|
|
8
8
|
import pixeltable.type_system as ts
|
|
9
9
|
from pixeltable import catalog
|
|
10
|
+
|
|
10
11
|
from .column_ref import ColumnRef
|
|
11
12
|
from .data_row import DataRow
|
|
12
13
|
from .expr import Expr
|
|
@@ -19,11 +20,13 @@ class ColumnPropertyRef(Expr):
|
|
|
19
20
|
|
|
20
21
|
The properties themselves are type-specific and may or may not need to reference the underlying column data.
|
|
21
22
|
"""
|
|
23
|
+
|
|
22
24
|
class Property(enum.Enum):
|
|
23
25
|
ERRORTYPE = 0
|
|
24
26
|
ERRORMSG = 1
|
|
25
27
|
FILEURL = 2
|
|
26
28
|
LOCALPATH = 3
|
|
29
|
+
CELLMD = 4 # JSON metadata for the cell, e.g. errortype, errormsg for media columns
|
|
27
30
|
|
|
28
31
|
def __init__(self, col_ref: ColumnRef, prop: Property):
|
|
29
32
|
super().__init__(ts.StringType(nullable=True))
|
|
@@ -31,69 +34,81 @@ class ColumnPropertyRef(Expr):
|
|
|
31
34
|
self.prop = prop
|
|
32
35
|
self.id = self._create_id()
|
|
33
36
|
|
|
34
|
-
def default_column_name(self) ->
|
|
37
|
+
def default_column_name(self) -> str | None:
|
|
35
38
|
return str(self).replace('.', '_')
|
|
36
39
|
|
|
37
40
|
def _equals(self, other: ColumnPropertyRef) -> bool:
|
|
38
41
|
return self.prop == other.prop
|
|
39
42
|
|
|
40
43
|
def _id_attrs(self) -> list[tuple[str, Any]]:
|
|
41
|
-
return super()._id_attrs()
|
|
44
|
+
return [*super()._id_attrs(), ('prop', self.prop.value)]
|
|
42
45
|
|
|
43
46
|
@property
|
|
44
|
-
def
|
|
47
|
+
def col_ref(self) -> ColumnRef:
|
|
45
48
|
col_ref = self.components[0]
|
|
46
49
|
assert isinstance(col_ref, ColumnRef)
|
|
47
50
|
return col_ref
|
|
48
51
|
|
|
49
52
|
def __repr__(self) -> str:
|
|
50
|
-
return f'{self.
|
|
53
|
+
return f'{self.col_ref}.{self.prop.name.lower()}'
|
|
51
54
|
|
|
52
|
-
def
|
|
53
|
-
return self.prop
|
|
55
|
+
def is_cellmd_prop(self) -> bool:
|
|
56
|
+
return self.prop in (self.Property.ERRORTYPE, self.Property.ERRORMSG, self.Property.CELLMD)
|
|
54
57
|
|
|
55
|
-
def sql_expr(self, sql_elements: SqlElementCache) ->
|
|
56
|
-
if not self.
|
|
58
|
+
def sql_expr(self, sql_elements: SqlElementCache) -> sql.ColumnElement | None:
|
|
59
|
+
if not self.col_ref.col_handle.get().is_stored:
|
|
57
60
|
return None
|
|
61
|
+
col = self.col_ref.col_handle.get()
|
|
58
62
|
|
|
59
63
|
# the errortype/-msg properties of a read-validated media column need to be extracted from the DataRow
|
|
60
64
|
if (
|
|
61
|
-
|
|
62
|
-
and
|
|
63
|
-
and self.
|
|
65
|
+
col.col_type.is_media_type()
|
|
66
|
+
and col.media_validation == catalog.MediaValidation.ON_READ
|
|
67
|
+
and self.is_cellmd_prop()
|
|
64
68
|
):
|
|
65
69
|
return None
|
|
66
70
|
|
|
67
71
|
if self.prop == self.Property.ERRORTYPE:
|
|
68
|
-
|
|
69
|
-
return self._col_ref.col.sa_errortype_col
|
|
72
|
+
return col.sa_cellmd_col.op('->>')('errortype')
|
|
70
73
|
if self.prop == self.Property.ERRORMSG:
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
return col.sa_cellmd_col.op('->>')('errormsg')
|
|
75
|
+
if self.prop == self.Property.CELLMD:
|
|
76
|
+
assert col.sa_cellmd_col is not None
|
|
77
|
+
return col.sa_cellmd_col
|
|
73
78
|
if self.prop == self.Property.FILEURL:
|
|
74
79
|
# the file url is stored as the column value
|
|
75
|
-
return sql_elements.get(self.
|
|
80
|
+
return sql_elements.get(self.col_ref)
|
|
76
81
|
return None
|
|
77
82
|
|
|
83
|
+
@classmethod
|
|
84
|
+
def create_cellmd_exc(cls, exc: Exception) -> dict[str, str]:
|
|
85
|
+
"""Create a cellmd value from an exception."""
|
|
86
|
+
return {'errortype': type(exc).__name__, 'errormsg': str(exc)}
|
|
87
|
+
|
|
78
88
|
def eval(self, data_row: DataRow, row_builder: RowBuilder) -> None:
|
|
79
89
|
if self.prop == self.Property.FILEURL:
|
|
80
|
-
assert data_row.has_val[self.
|
|
81
|
-
data_row[self.slot_idx] = data_row.file_urls[self.
|
|
90
|
+
assert data_row.has_val[self.col_ref.slot_idx]
|
|
91
|
+
data_row[self.slot_idx] = data_row.file_urls[self.col_ref.slot_idx]
|
|
82
92
|
return
|
|
83
93
|
elif self.prop == self.Property.LOCALPATH:
|
|
84
|
-
assert data_row.has_val[self.
|
|
85
|
-
data_row[self.slot_idx] = data_row.file_paths[self.
|
|
94
|
+
assert data_row.has_val[self.col_ref.slot_idx]
|
|
95
|
+
data_row[self.slot_idx] = data_row.file_paths[self.col_ref.slot_idx]
|
|
86
96
|
return
|
|
87
|
-
elif self.
|
|
88
|
-
exc = data_row.get_exc(self.
|
|
97
|
+
elif self.is_cellmd_prop():
|
|
98
|
+
exc = data_row.get_exc(self.col_ref.slot_idx)
|
|
89
99
|
if exc is None:
|
|
90
100
|
data_row[self.slot_idx] = None
|
|
91
101
|
elif self.prop == self.Property.ERRORTYPE:
|
|
92
102
|
data_row[self.slot_idx] = type(exc).__name__
|
|
93
|
-
|
|
103
|
+
elif self.prop == self.Property.ERRORMSG:
|
|
94
104
|
data_row[self.slot_idx] = str(exc)
|
|
105
|
+
elif self.prop == self.Property.CELLMD:
|
|
106
|
+
data_row[self.slot_idx] = self.create_cellmd_exc(exc)
|
|
107
|
+
else:
|
|
108
|
+
raise AssertionError(f'Unknown property {self.prop}')
|
|
109
|
+
return
|
|
95
110
|
else:
|
|
96
|
-
|
|
111
|
+
raise AssertionError()
|
|
97
112
|
|
|
98
113
|
def _as_dict(self) -> dict:
|
|
99
114
|
return {'prop': self.prop.value, **super()._as_dict()}
|
|
@@ -103,4 +118,3 @@ class ColumnPropertyRef(Expr):
|
|
|
103
118
|
assert 'prop' in d
|
|
104
119
|
assert isinstance(components[0], ColumnRef)
|
|
105
120
|
return cls(components[0], cls.Property(d['prop']))
|
|
106
|
-
|