pytrilogy 0.0.3.88__py3-none-any.whl → 0.0.3.90__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 pytrilogy might be problematic. Click here for more details.
- {pytrilogy-0.0.3.88.dist-info → pytrilogy-0.0.3.90.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.3.88.dist-info → pytrilogy-0.0.3.90.dist-info}/RECORD +15 -15
- trilogy/__init__.py +1 -1
- trilogy/core/functions.py +2 -1
- trilogy/core/models/author.py +36 -3
- trilogy/core/models/core.py +27 -8
- trilogy/core/processing/node_generators/filter_node.py +9 -3
- trilogy/dialect/base.py +1 -1
- trilogy/dialect/bigquery.py +1 -1
- trilogy/executor.py +12 -4
- trilogy/parsing/parse_engine.py +10 -1
- {pytrilogy-0.0.3.88.dist-info → pytrilogy-0.0.3.90.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.88.dist-info → pytrilogy-0.0.3.90.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.88.dist-info → pytrilogy-0.0.3.90.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.88.dist-info → pytrilogy-0.0.3.90.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
pytrilogy-0.0.3.
|
|
2
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
pytrilogy-0.0.3.90.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
2
|
+
trilogy/__init__.py,sha256=xsnAVhMdPDgMBudr3tOEYEMfxl0t6RWAK_231sFSxAU,303
|
|
3
3
|
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
trilogy/constants.py,sha256=eKb_EJvSqjN9tGbdVEViwdtwwh8fZ3-jpOEDqL71y70,1691
|
|
5
5
|
trilogy/engine.py,sha256=OK2RuqCIUId6yZ5hfF8J1nxGP0AJqHRZiafcowmW0xc,1728
|
|
6
|
-
trilogy/executor.py,sha256=
|
|
6
|
+
trilogy/executor.py,sha256=tcowEz8I7zbwLnuTr7BlGJ5wnt1JKBNffXbm5ywkNv8,17032
|
|
7
7
|
trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
|
|
8
8
|
trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
trilogy/render.py,sha256=qQWwduymauOlB517UtM-VGbVe8Cswa4UJub5aGbSO6c,1512
|
|
@@ -16,17 +16,17 @@ trilogy/core/env_processor.py,sha256=pFsxnluKIusGKx1z7tTnfsd_xZcPy9pZDungkjkyvI0
|
|
|
16
16
|
trilogy/core/environment_helpers.py,sha256=VvPIiFemqaLLpIpLIqprfu63K7muZ1YzNg7UZIUph8w,8267
|
|
17
17
|
trilogy/core/ergonomics.py,sha256=e-7gE29vPLFdg0_A1smQ7eOrUwKl5VYdxRSTddHweRA,1631
|
|
18
18
|
trilogy/core/exceptions.py,sha256=jYEduuMehcMkmCpf-OC_taELPZm7qNfeSNzIWkDYScs,707
|
|
19
|
-
trilogy/core/functions.py,sha256=
|
|
19
|
+
trilogy/core/functions.py,sha256=hnfcNjAD-XQ572vEwuUEAdBf8zKFWYwPeHIpESjUpZs,32928
|
|
20
20
|
trilogy/core/graph_models.py,sha256=BYhJzHKSgnZHVLJs1CfsgrxTPHqKqPNeA64RlozGY0A,3498
|
|
21
21
|
trilogy/core/internal.py,sha256=wFx4e1I0mtx159YFShSXeUBSQ82NINtAbOI-92RX4i8,2151
|
|
22
22
|
trilogy/core/optimization.py,sha256=ojpn-p79lr03SSVQbbw74iPCyoYpDYBmj1dbZ3oXCjI,8860
|
|
23
23
|
trilogy/core/query_processor.py,sha256=5aFgv-2LVM1Uku9cR_tFuTRDwyLnxc95bCMAHeFy2AY,20332
|
|
24
24
|
trilogy/core/utility.py,sha256=3VC13uSQWcZNghgt7Ot0ZTeEmNqs__cx122abVq9qhM,410
|
|
25
25
|
trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
-
trilogy/core/models/author.py,sha256=
|
|
26
|
+
trilogy/core/models/author.py,sha256=tcsr42hHiQ2PHh2_le9sr5IV5nv3twxUv2EXr5iDGxg,80201
|
|
27
27
|
trilogy/core/models/build.py,sha256=CyrSo4xgU-uDKW3xUVYs5cTk3Z3Z2BMWdGQNHnHZOqU,66127
|
|
28
28
|
trilogy/core/models/build_environment.py,sha256=s_C9xAHuD3yZ26T15pWVBvoqvlp2LdZ8yjsv2_HdXLk,5363
|
|
29
|
-
trilogy/core/models/core.py,sha256=
|
|
29
|
+
trilogy/core/models/core.py,sha256=NOvonI4Ip4thpz5WoJZWbbBa44PFfpd2hXGx2Cbi4CE,12521
|
|
30
30
|
trilogy/core/models/datasource.py,sha256=wogTevZ-9CyUW2a8gjzqMCieircxi-J5lkI7EOAZnck,9596
|
|
31
31
|
trilogy/core/models/environment.py,sha256=0IHSCFf5e5b4LPQN3vmjumtfM1iD1tN4WMoUr0UqxZI,27855
|
|
32
32
|
trilogy/core/models/execute.py,sha256=sVWhjwWull-T6pUJizhrYVGCWHY3eZivVN6KNlhcHig,41839
|
|
@@ -46,7 +46,7 @@ trilogy/core/processing/node_generators/__init__.py,sha256=iVJ-crowPxYeut-hFjyEj
|
|
|
46
46
|
trilogy/core/processing/node_generators/basic_node.py,sha256=TLZCv4WS196a-0g5xgKuJGthnGP8Ugm46iz85_3NIY4,5626
|
|
47
47
|
trilogy/core/processing/node_generators/common.py,sha256=PdysdroW9DUADP7f5Wv_GKPUyCTROZV1g3L45fawxi8,9443
|
|
48
48
|
trilogy/core/processing/node_generators/constant_node.py,sha256=LfpDq2WrBRZ3tGsLxw77LuigKfhbteWWh9L8BGdMGwk,1146
|
|
49
|
-
trilogy/core/processing/node_generators/filter_node.py,sha256=
|
|
49
|
+
trilogy/core/processing/node_generators/filter_node.py,sha256=ArBsQJl-4fWBJWCE28CRQ7UT7ErnFfbcseoQQZrBodY,11220
|
|
50
50
|
trilogy/core/processing/node_generators/group_node.py,sha256=1QJhRxsTklJ5xq8wHlAURZaN9gL9FPpeCa1OJ7IwXnY,6769
|
|
51
51
|
trilogy/core/processing/node_generators/group_to_node.py,sha256=jKcNCDOY6fNblrdZwaRU0sbUSr9H0moQbAxrGgX6iGA,3832
|
|
52
52
|
trilogy/core/processing/node_generators/multiselect_node.py,sha256=GWV5yLmKTe1yyPhN60RG1Rnrn4ktfn9lYYXi_FVU4UI,7061
|
|
@@ -77,8 +77,8 @@ trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
|
77
77
|
trilogy/core/statements/common.py,sha256=KxEmz2ySySyZ6CTPzn0fJl5NX2KOk1RPyuUSwWhnK1g,759
|
|
78
78
|
trilogy/core/statements/execute.py,sha256=pfr1CZ_Cx1qQ-7LDyRI0JUfvtxBr_GGv-VeqiAjr43g,1406
|
|
79
79
|
trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
80
|
-
trilogy/dialect/base.py,sha256=
|
|
81
|
-
trilogy/dialect/bigquery.py,sha256=
|
|
80
|
+
trilogy/dialect/base.py,sha256=_L5wHBHz8v4Us3tu4QIupKuaObmyhWhyDuroT95wUbo,48228
|
|
81
|
+
trilogy/dialect/bigquery.py,sha256=XS3hpybeowgfrOrkycAigAF3NX2YUzTzfgE6f__2fT4,4316
|
|
82
82
|
trilogy/dialect/common.py,sha256=tSthIZOXXRPQ4KeMKnDDsH7KlTmf2EVqigVtLyoc4zc,6071
|
|
83
83
|
trilogy/dialect/config.py,sha256=olnyeVU5W5T6b9-dMeNAnvxuPlyc2uefb7FRME094Ec,3834
|
|
84
84
|
trilogy/dialect/dataframe.py,sha256=RUbNgReEa9g3pL6H7fP9lPTrAij5pkqedpZ99D8_5AE,1522
|
|
@@ -98,7 +98,7 @@ trilogy/parsing/common.py,sha256=yV1AckK0h8u1OFeGQBTMu-wuW5m63c5CcZuPicsTH_w,306
|
|
|
98
98
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
99
99
|
trilogy/parsing/exceptions.py,sha256=Xwwsv2C9kSNv2q-HrrKC1f60JNHShXcCMzstTSEbiCw,154
|
|
100
100
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
101
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
101
|
+
trilogy/parsing/parse_engine.py,sha256=fgqCtV6sf9HrkViEjf6XXdRpPf4hJ1gSyzLXZ9sLBHs,80148
|
|
102
102
|
trilogy/parsing/render.py,sha256=HSNntD82GiiwHT-TWPLXAaIMWLYVV5B5zQEsgwrHIBE,19605
|
|
103
103
|
trilogy/parsing/trilogy.lark,sha256=ySzMMLxyPjn74MjFHZxXPTW-jHW68KLPJpiszPvZaO0,15780
|
|
104
104
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -111,8 +111,8 @@ trilogy/std/money.preql,sha256=XWwvAV3WxBsHX9zfptoYRnBigcfYwrYtBHXTME0xJuQ,2082
|
|
|
111
111
|
trilogy/std/net.preql,sha256=WZCuvH87_rZntZiuGJMmBDMVKkdhTtxeHOkrXNwJ1EE,416
|
|
112
112
|
trilogy/std/ranking.preql,sha256=LDoZrYyz4g3xsII9XwXfmstZD-_92i1Eox1UqkBIfi8,83
|
|
113
113
|
trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
|
|
114
|
-
pytrilogy-0.0.3.
|
|
115
|
-
pytrilogy-0.0.3.
|
|
116
|
-
pytrilogy-0.0.3.
|
|
117
|
-
pytrilogy-0.0.3.
|
|
118
|
-
pytrilogy-0.0.3.
|
|
114
|
+
pytrilogy-0.0.3.90.dist-info/METADATA,sha256=X046-UiVgiZTCb6DQXtQuzAe9Qm4DNTefqbNXixNx5g,9589
|
|
115
|
+
pytrilogy-0.0.3.90.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
116
|
+
pytrilogy-0.0.3.90.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
|
|
117
|
+
pytrilogy-0.0.3.90.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
118
|
+
pytrilogy-0.0.3.90.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/functions.py
CHANGED
|
@@ -92,7 +92,7 @@ def get_attr_datatype(
|
|
|
92
92
|
lookup = args[1]
|
|
93
93
|
datatype = arg_to_datatype(arg)
|
|
94
94
|
if isinstance(datatype, StructType):
|
|
95
|
-
return
|
|
95
|
+
return datatype.field_types[lookup]
|
|
96
96
|
return datatype
|
|
97
97
|
|
|
98
98
|
|
|
@@ -935,6 +935,7 @@ class FunctionFactory:
|
|
|
935
935
|
output_purpose = Purpose.METRIC
|
|
936
936
|
else:
|
|
937
937
|
output_purpose = Purpose.PROPERTY
|
|
938
|
+
|
|
938
939
|
return Function(
|
|
939
940
|
operator=operator,
|
|
940
941
|
arguments=full_args, # type: ignore
|
trilogy/core/models/author.py
CHANGED
|
@@ -172,11 +172,16 @@ class ConceptRef(Addressable, Namespaced, DataTyped, Mergeable, BaseModel):
|
|
|
172
172
|
for candidate in candidates:
|
|
173
173
|
if not candidate.startswith(f"{source}."):
|
|
174
174
|
continue
|
|
175
|
+
attribute = self.address.rsplit(".", 1)[1]
|
|
176
|
+
dtype = arg_to_datatype(target)
|
|
177
|
+
if not isinstance(dtype, StructType):
|
|
178
|
+
continue
|
|
179
|
+
output_type = dtype.field_types.get(attribute, DataType.UNKNOWN)
|
|
175
180
|
return Function(
|
|
176
181
|
arguments=[target, self.address.rsplit(".", 1)[1]],
|
|
177
182
|
operator=FunctionType.ATTR_ACCESS,
|
|
178
183
|
arg_count=2,
|
|
179
|
-
output_datatype=
|
|
184
|
+
output_datatype=output_type,
|
|
180
185
|
output_purpose=Purpose.PROPERTY,
|
|
181
186
|
)
|
|
182
187
|
return self
|
|
@@ -654,6 +659,12 @@ class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
|
|
|
654
659
|
def validate_comparison(self):
|
|
655
660
|
left_type = arg_to_datatype(self.left)
|
|
656
661
|
right_type = arg_to_datatype(self.right)
|
|
662
|
+
left_name = (
|
|
663
|
+
left_type.name if isinstance(left_type, DataType) else str(left_type)
|
|
664
|
+
)
|
|
665
|
+
right_name = (
|
|
666
|
+
right_type.name if isinstance(right_type, DataType) else str(right_type)
|
|
667
|
+
)
|
|
657
668
|
if self.operator in (ComparisonOperator.IS, ComparisonOperator.IS_NOT):
|
|
658
669
|
if self.right != MagicConstants.NULL and DataType.BOOL != right_type:
|
|
659
670
|
raise SyntaxError(
|
|
@@ -671,12 +682,12 @@ class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
|
|
|
671
682
|
left_type, right_type
|
|
672
683
|
):
|
|
673
684
|
raise SyntaxError(
|
|
674
|
-
f"Cannot compare {
|
|
685
|
+
f"Cannot compare {left_name} and {right_name} with operator {self.operator} in {str(self)}"
|
|
675
686
|
)
|
|
676
687
|
else:
|
|
677
688
|
if not is_compatible_datatype(left_type, right_type):
|
|
678
689
|
raise SyntaxError(
|
|
679
|
-
f"Cannot compare {
|
|
690
|
+
f"Cannot compare {left_name} ({self.left}) and {right_name} ({self.right}) of different types with operator {self.operator.value} in {str(self)}"
|
|
680
691
|
)
|
|
681
692
|
|
|
682
693
|
return self
|
|
@@ -1637,6 +1648,9 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1637
1648
|
] = None
|
|
1638
1649
|
arguments: Sequence[FuncArgs]
|
|
1639
1650
|
|
|
1651
|
+
class Config:
|
|
1652
|
+
frozen = True
|
|
1653
|
+
|
|
1640
1654
|
def __repr__(self):
|
|
1641
1655
|
return f'{self.operator.value}({",".join([str(a) for a in self.arguments])})'
|
|
1642
1656
|
|
|
@@ -1647,6 +1661,16 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1647
1661
|
def datatype(self):
|
|
1648
1662
|
return self.output_datatype
|
|
1649
1663
|
|
|
1664
|
+
@field_validator("output_datatype")
|
|
1665
|
+
@classmethod
|
|
1666
|
+
def parse_output_datatype(cls, v, info: ValidationInfo):
|
|
1667
|
+
values = info.data
|
|
1668
|
+
if values.get("operator") == FunctionType.ATTR_ACCESS:
|
|
1669
|
+
print(v)
|
|
1670
|
+
if isinstance(v, StructType):
|
|
1671
|
+
raise SyntaxError
|
|
1672
|
+
return v
|
|
1673
|
+
|
|
1650
1674
|
@field_validator("arguments", mode="before")
|
|
1651
1675
|
@classmethod
|
|
1652
1676
|
def parse_arguments(cls, v, info: ValidationInfo):
|
|
@@ -1745,8 +1769,16 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1745
1769
|
]
|
|
1746
1770
|
if self.output_datatype == DataType.UNKNOWN:
|
|
1747
1771
|
new_output = merge_datatypes([arg_to_datatype(x) for x in nargs])
|
|
1772
|
+
|
|
1773
|
+
if self.operator == FunctionType.ATTR_ACCESS:
|
|
1774
|
+
if isinstance(new_output, StructType):
|
|
1775
|
+
new_output = new_output.field_types[str(nargs[1])]
|
|
1748
1776
|
else:
|
|
1749
1777
|
new_output = self.output_datatype
|
|
1778
|
+
# this is not ideal - see hacky logic for datatypes above
|
|
1779
|
+
# we need to figure out how to patch properly
|
|
1780
|
+
# should use function factory, but does not have environment access
|
|
1781
|
+
# probably move all datatype resolution to build?
|
|
1750
1782
|
return Function.model_construct(
|
|
1751
1783
|
operator=self.operator,
|
|
1752
1784
|
arguments=nargs,
|
|
@@ -2444,6 +2476,7 @@ FuncArgs = (
|
|
|
2444
2476
|
| CaseElse
|
|
2445
2477
|
| WindowItem
|
|
2446
2478
|
| FilterItem
|
|
2479
|
+
| bool
|
|
2447
2480
|
| int
|
|
2448
2481
|
| float
|
|
2449
2482
|
| DatePart
|
trilogy/core/models/core.py
CHANGED
|
@@ -244,6 +244,19 @@ class StructType(BaseModel):
|
|
|
244
244
|
def value(self):
|
|
245
245
|
return self.data_type.value
|
|
246
246
|
|
|
247
|
+
@property
|
|
248
|
+
def field_types(self) -> Dict[str, CONCRETE_TYPES]:
|
|
249
|
+
out: Dict[str, CONCRETE_TYPES] = {}
|
|
250
|
+
keys = list(self.fields_map.keys())
|
|
251
|
+
for idx, field in enumerate(self.fields):
|
|
252
|
+
if isinstance(field, StructComponent):
|
|
253
|
+
out[field.name] = arg_to_datatype(field.type)
|
|
254
|
+
elif isinstance(field, DataTyped):
|
|
255
|
+
out[keys[idx]] = field.output_datatype
|
|
256
|
+
else:
|
|
257
|
+
out[keys[idx]] = field
|
|
258
|
+
return out
|
|
259
|
+
|
|
247
260
|
def __hash__(self):
|
|
248
261
|
return hash(str(self))
|
|
249
262
|
|
|
@@ -251,9 +264,10 @@ class StructType(BaseModel):
|
|
|
251
264
|
class ListWrapper(Generic[VT], UserList):
|
|
252
265
|
"""Used to distinguish parsed list objects from other lists"""
|
|
253
266
|
|
|
254
|
-
def __init__(self, *args, type: DataType, **kwargs):
|
|
267
|
+
def __init__(self, *args, type: DataType, nullable: bool = False, **kwargs):
|
|
255
268
|
super().__init__(*args, **kwargs)
|
|
256
269
|
self.type = type
|
|
270
|
+
self.nullable = nullable
|
|
257
271
|
|
|
258
272
|
@classmethod
|
|
259
273
|
def __get_pydantic_core_schema__(
|
|
@@ -302,10 +316,11 @@ class MapWrapper(Generic[KT, VT], UserDict):
|
|
|
302
316
|
class TupleWrapper(Generic[VT], tuple):
|
|
303
317
|
"""Used to distinguish parsed tuple objects from other tuples"""
|
|
304
318
|
|
|
305
|
-
def __init__(self, val, type: DataType, **kwargs):
|
|
319
|
+
def __init__(self, val, type: DataType, nullable: bool = False, **kwargs):
|
|
306
320
|
super().__init__()
|
|
307
321
|
self.type = type
|
|
308
322
|
self.val = val
|
|
323
|
+
self.nullable = nullable
|
|
309
324
|
|
|
310
325
|
def __getnewargs__(self):
|
|
311
326
|
return (self.val, self.type)
|
|
@@ -331,15 +346,19 @@ class TupleWrapper(Generic[VT], tuple):
|
|
|
331
346
|
|
|
332
347
|
|
|
333
348
|
def list_to_wrapper(args):
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
349
|
+
rtypes = [arg_to_datatype(arg) for arg in args]
|
|
350
|
+
types = [arg for arg in rtypes if arg != DataType.NULL]
|
|
351
|
+
if not len(set(types)) == 1:
|
|
352
|
+
raise SyntaxError(f"Cannot create a list with this set of types: {set(types)}")
|
|
353
|
+
return ListWrapper(args, type=types[0], nullable=DataType.NULL in rtypes)
|
|
337
354
|
|
|
338
355
|
|
|
339
356
|
def tuple_to_wrapper(args):
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
357
|
+
rtypes = [arg_to_datatype(arg) for arg in args]
|
|
358
|
+
types = [arg for arg in rtypes if arg != DataType.NULL]
|
|
359
|
+
if not len(set(types)) == 1:
|
|
360
|
+
raise SyntaxError(f"Cannot create a tuple with this set of types: {set(types)}")
|
|
361
|
+
return TupleWrapper(args, type=types[0], nullable=DataType.NULL in rtypes)
|
|
343
362
|
|
|
344
363
|
|
|
345
364
|
def dict_to_map_wrapper(arg):
|
|
@@ -77,11 +77,17 @@ def build_parent_concepts(
|
|
|
77
77
|
True if (conditions and conditions == filter_where) else False
|
|
78
78
|
)
|
|
79
79
|
|
|
80
|
+
exact_partial_matches = True
|
|
80
81
|
for x in local_optional:
|
|
81
82
|
if isinstance(x.lineage, FILTER_TYPES):
|
|
82
|
-
if
|
|
83
|
+
if set([x.address for x in x.lineage.where.concept_arguments]) == set(
|
|
84
|
+
[x.address for x in filter_where.concept_arguments]
|
|
85
|
+
):
|
|
86
|
+
exact_partial_matches = (
|
|
87
|
+
exact_partial_matches and x.lineage.where == filter_where
|
|
88
|
+
)
|
|
83
89
|
logger.info(
|
|
84
|
-
f"{padding(depth)}{LOGGER_PREFIX} fetching parents for peer {x}
|
|
90
|
+
f"{padding(depth)}{LOGGER_PREFIX} fetching parents for peer {x.address} (of {concept.address})"
|
|
85
91
|
)
|
|
86
92
|
|
|
87
93
|
for arg in x.lineage.content_concept_arguments:
|
|
@@ -100,7 +106,7 @@ def build_parent_concepts(
|
|
|
100
106
|
if x.address in same_filter_optional:
|
|
101
107
|
continue
|
|
102
108
|
extra_row_level_optional.append(x)
|
|
103
|
-
is_optimized_pushdown = pushdown_filter_to_parent(
|
|
109
|
+
is_optimized_pushdown = exact_partial_matches and pushdown_filter_to_parent(
|
|
104
110
|
local_optional, conditions, filter_where, same_filter_optional, depth
|
|
105
111
|
)
|
|
106
112
|
if not is_optimized_pushdown:
|
trilogy/dialect/base.py
CHANGED
|
@@ -174,7 +174,7 @@ FUNCTION_MAP = {
|
|
|
174
174
|
FunctionType.CAST: lambda x: f"cast({x[0]} as {x[1]})",
|
|
175
175
|
FunctionType.CASE: lambda x: render_case(x),
|
|
176
176
|
FunctionType.SPLIT: lambda x: f"split({x[0]}, {x[1]})",
|
|
177
|
-
FunctionType.IS_NULL: lambda x: f"
|
|
177
|
+
FunctionType.IS_NULL: lambda x: f"{x[0]} is null",
|
|
178
178
|
FunctionType.BOOL: lambda x: f"CASE WHEN {x[0]} THEN TRUE ELSE FALSE END",
|
|
179
179
|
FunctionType.PARENTHETICAL: lambda x: f"({x[0]})",
|
|
180
180
|
# Complex
|
trilogy/dialect/bigquery.py
CHANGED
|
@@ -24,7 +24,7 @@ FUNCTION_MAP = {
|
|
|
24
24
|
FunctionType.LIKE: lambda x: (
|
|
25
25
|
f" CASE WHEN {x[0]} like {x[1]} THEN True ELSE False END"
|
|
26
26
|
),
|
|
27
|
-
FunctionType.IS_NULL: lambda x: f"
|
|
27
|
+
FunctionType.IS_NULL: lambda x: f"{x[0]} IS NULL",
|
|
28
28
|
FunctionType.MINUTE: lambda x: f"EXTRACT(MINUTE from {x[0]})",
|
|
29
29
|
FunctionType.SECOND: lambda x: f"EXTRACT(SECOND from {x[0]})",
|
|
30
30
|
FunctionType.HOUR: lambda x: f"EXTRACT(HOUR from {x[0]})",
|
trilogy/executor.py
CHANGED
|
@@ -6,7 +6,7 @@ from typing import Any, Generator, List, Optional, Protocol
|
|
|
6
6
|
from sqlalchemy import text
|
|
7
7
|
from sqlalchemy.engine import CursorResult
|
|
8
8
|
|
|
9
|
-
from trilogy.constants import Rendering, logger
|
|
9
|
+
from trilogy.constants import MagicConstants, Rendering, logger
|
|
10
10
|
from trilogy.core.enums import FunctionType, Granularity, IOType
|
|
11
11
|
from trilogy.core.models.author import Concept, ConceptRef, Function
|
|
12
12
|
from trilogy.core.models.build import BuildFunction
|
|
@@ -388,6 +388,11 @@ class Executor(object):
|
|
|
388
388
|
if persist and isinstance(x, ProcessedQueryPersist):
|
|
389
389
|
self.environment.add_datasource(x.datasource)
|
|
390
390
|
|
|
391
|
+
def _atom_to_value(self, val: Any) -> Any:
|
|
392
|
+
if val == MagicConstants.NULL:
|
|
393
|
+
return None
|
|
394
|
+
return val
|
|
395
|
+
|
|
391
396
|
def _concept_to_value(
|
|
392
397
|
self,
|
|
393
398
|
concept: Concept,
|
|
@@ -402,12 +407,15 @@ class Executor(object):
|
|
|
402
407
|
):
|
|
403
408
|
rval = concept.lineage.arguments[0]
|
|
404
409
|
if isinstance(rval, ListWrapper):
|
|
405
|
-
return [x for x in rval]
|
|
410
|
+
return [self._atom_to_value(x) for x in rval]
|
|
406
411
|
if isinstance(rval, MapWrapper):
|
|
407
412
|
# duckdb expects maps in this format as variables
|
|
408
413
|
if self.dialect == Dialects.DUCK_DB:
|
|
409
|
-
return {
|
|
410
|
-
|
|
414
|
+
return {
|
|
415
|
+
"key": [self._atom_to_value(x) for x in rval],
|
|
416
|
+
"value": [self._atom_to_value(rval[x]) for x in rval],
|
|
417
|
+
}
|
|
418
|
+
return {k: self._atom_to_value(v) for k, v in rval.items()}
|
|
411
419
|
# if isinstance(rval, ConceptRef):
|
|
412
420
|
# return self._concept_to_value(self.environment.concepts[rval.address], local_concepts=local_concepts)
|
|
413
421
|
return rval
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -701,6 +701,7 @@ class ParseToObjects(Transformer):
|
|
|
701
701
|
environment=self.environment,
|
|
702
702
|
metadata=metadata,
|
|
703
703
|
)
|
|
704
|
+
|
|
704
705
|
# let constant purposes exist to support round-tripping
|
|
705
706
|
# as a build concept may end up with a constant based on constant inlining happening recursively
|
|
706
707
|
if purpose == Purpose.KEY and concept.purpose != Purpose.KEY:
|
|
@@ -922,7 +923,6 @@ class ParseToObjects(Transformer):
|
|
|
922
923
|
name=output,
|
|
923
924
|
metadata=metadata,
|
|
924
925
|
)
|
|
925
|
-
|
|
926
926
|
return ConceptTransform(function=transformation, output=concept)
|
|
927
927
|
|
|
928
928
|
@v_args(meta=True)
|
|
@@ -2040,6 +2040,15 @@ class ParseToObjects(Transformer):
|
|
|
2040
2040
|
|
|
2041
2041
|
@v_args(meta=True)
|
|
2042
2042
|
def fnot(self, meta, args):
|
|
2043
|
+
if arg_to_datatype(args[0]) == DataType.BOOL:
|
|
2044
|
+
return Comparison(
|
|
2045
|
+
left=self.function_factory.create_function(
|
|
2046
|
+
[args[0], False], FunctionType.COALESCE, meta
|
|
2047
|
+
),
|
|
2048
|
+
operator=ComparisonOperator.EQ,
|
|
2049
|
+
right=False,
|
|
2050
|
+
meta=meta,
|
|
2051
|
+
)
|
|
2043
2052
|
return self.function_factory.create_function(args, FunctionType.IS_NULL, meta)
|
|
2044
2053
|
|
|
2045
2054
|
@v_args(meta=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|