pytrilogy 0.0.3.91__py3-none-any.whl → 0.0.3.93__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.91.dist-info → pytrilogy-0.0.3.93.dist-info}/METADATA +9 -6
- {pytrilogy-0.0.3.91.dist-info → pytrilogy-0.0.3.93.dist-info}/RECORD +20 -19
- trilogy/__init__.py +1 -1
- trilogy/core/env_processor.py +31 -9
- trilogy/core/graph_models.py +11 -1
- trilogy/core/models/author.py +49 -15
- trilogy/core/models/build.py +321 -185
- trilogy/core/models/build_environment.py +2 -6
- trilogy/core/models/core.py +12 -0
- trilogy/core/models/execute.py +3 -3
- trilogy/core/processing/node_generators/group_node.py +3 -7
- trilogy/core/processing/node_generators/node_merge_node.py +25 -23
- trilogy/core/processing/node_generators/select_merge_node_v2.py +792 -0
- trilogy/parsing/common.py +50 -29
- trilogy/parsing/parse_engine.py +22 -3
- trilogy/parsing/trilogy.lark +3 -1
- {pytrilogy-0.0.3.91.dist-info → pytrilogy-0.0.3.93.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.91.dist-info → pytrilogy-0.0.3.93.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.91.dist-info → pytrilogy-0.0.3.93.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.91.dist-info → pytrilogy-0.0.3.93.dist-info}/top_level.txt +0 -0
trilogy/core/models/build.py
CHANGED
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC
|
|
4
4
|
from collections import defaultdict
|
|
5
|
+
from dataclasses import dataclass, field
|
|
5
6
|
from datetime import date, datetime
|
|
6
7
|
from functools import cached_property, singledispatchmethod
|
|
7
8
|
from typing import (
|
|
@@ -18,12 +19,7 @@ from typing import (
|
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
from pydantic import (
|
|
21
|
-
BaseModel,
|
|
22
22
|
ConfigDict,
|
|
23
|
-
Field,
|
|
24
|
-
ValidationInfo,
|
|
25
|
-
computed_field,
|
|
26
|
-
field_validator,
|
|
27
23
|
)
|
|
28
24
|
|
|
29
25
|
from trilogy.constants import DEFAULT_NAMESPACE, MagicConstants
|
|
@@ -93,7 +89,6 @@ from trilogy.core.models.datasource import (
|
|
|
93
89
|
RawColumnExpr,
|
|
94
90
|
)
|
|
95
91
|
from trilogy.core.models.environment import Environment
|
|
96
|
-
from trilogy.utility import unique
|
|
97
92
|
|
|
98
93
|
# TODO: refactor to avoid these
|
|
99
94
|
if TYPE_CHECKING:
|
|
@@ -149,8 +144,8 @@ def concept_is_relevant(
|
|
|
149
144
|
|
|
150
145
|
def concepts_to_build_grain_concepts(
|
|
151
146
|
concepts: Iterable[BuildConcept | str], environment: "BuildEnvironment" | None
|
|
152
|
-
) ->
|
|
153
|
-
pconcepts = []
|
|
147
|
+
) -> set[str]:
|
|
148
|
+
pconcepts: list[BuildConcept] = []
|
|
154
149
|
for c in concepts:
|
|
155
150
|
if isinstance(c, BuildConcept):
|
|
156
151
|
pconcepts.append(c)
|
|
@@ -162,27 +157,23 @@ def concepts_to_build_grain_concepts(
|
|
|
162
157
|
f"Unable to resolve input {c} without environment provided to concepts_to_grain call"
|
|
163
158
|
)
|
|
164
159
|
|
|
165
|
-
final:
|
|
160
|
+
final: set[str] = set()
|
|
166
161
|
for sub in pconcepts:
|
|
167
162
|
if not concept_is_relevant(sub, pconcepts):
|
|
168
163
|
continue
|
|
169
|
-
final.
|
|
170
|
-
final = unique(final, "address")
|
|
171
|
-
v2 = sorted(final, key=lambda x: x.name)
|
|
172
|
-
return v2
|
|
164
|
+
final.add(sub.address)
|
|
173
165
|
|
|
166
|
+
return final
|
|
174
167
|
|
|
175
|
-
|
|
168
|
+
|
|
169
|
+
@dataclass
|
|
170
|
+
class LooseBuildConceptList:
|
|
176
171
|
concepts: Sequence[BuildConcept]
|
|
177
172
|
|
|
178
173
|
@cached_property
|
|
179
174
|
def addresses(self) -> set[str]:
|
|
180
175
|
return {s.address for s in self.concepts}
|
|
181
176
|
|
|
182
|
-
@classmethod
|
|
183
|
-
def validate(cls, v):
|
|
184
|
-
return cls(v)
|
|
185
|
-
|
|
186
177
|
@cached_property
|
|
187
178
|
def sorted_addresses(self) -> List[str]:
|
|
188
179
|
return sorted(list(self.addresses))
|
|
@@ -250,7 +241,8 @@ def get_concept_arguments(expr) -> List["BuildConcept"]:
|
|
|
250
241
|
return output
|
|
251
242
|
|
|
252
243
|
|
|
253
|
-
|
|
244
|
+
@dataclass
|
|
245
|
+
class BuildParamaterizedConceptReference(DataTyped):
|
|
254
246
|
concept: BuildConcept
|
|
255
247
|
|
|
256
248
|
def __str__(self):
|
|
@@ -265,12 +257,17 @@ class BuildParamaterizedConceptReference(DataTyped, BaseModel):
|
|
|
265
257
|
return self.concept.output_datatype
|
|
266
258
|
|
|
267
259
|
|
|
268
|
-
|
|
269
|
-
|
|
260
|
+
@dataclass
|
|
261
|
+
class BuildGrain:
|
|
262
|
+
components: set[str] = field(default_factory=set)
|
|
270
263
|
where_clause: Optional[BuildWhereClause] = None
|
|
264
|
+
_str: str | None = None
|
|
265
|
+
_str_no_condition: str | None = None
|
|
271
266
|
|
|
272
267
|
def without_condition(self):
|
|
273
|
-
|
|
268
|
+
if not self.where_clause:
|
|
269
|
+
return self
|
|
270
|
+
return BuildGrain(components=self.components)
|
|
274
271
|
|
|
275
272
|
@classmethod
|
|
276
273
|
def from_concepts(
|
|
@@ -281,32 +278,12 @@ class BuildGrain(BaseModel):
|
|
|
281
278
|
) -> "BuildGrain":
|
|
282
279
|
|
|
283
280
|
return BuildGrain(
|
|
284
|
-
components=
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
concepts, environment=environment
|
|
288
|
-
)
|
|
289
|
-
},
|
|
281
|
+
components=concepts_to_build_grain_concepts(
|
|
282
|
+
concepts, environment=environment
|
|
283
|
+
),
|
|
290
284
|
where_clause=where_clause,
|
|
291
285
|
)
|
|
292
286
|
|
|
293
|
-
@field_validator("components", mode="before")
|
|
294
|
-
def component_validator(cls, v, info: ValidationInfo):
|
|
295
|
-
output = set()
|
|
296
|
-
if isinstance(v, list):
|
|
297
|
-
for vc in v:
|
|
298
|
-
if isinstance(vc, BuildConcept):
|
|
299
|
-
output.add(vc.address)
|
|
300
|
-
else:
|
|
301
|
-
output.add(vc)
|
|
302
|
-
else:
|
|
303
|
-
output = v
|
|
304
|
-
if not isinstance(output, set):
|
|
305
|
-
raise ValueError(f"Invalid grain component {output}, is not set")
|
|
306
|
-
if not all(isinstance(x, str) for x in output):
|
|
307
|
-
raise ValueError(f"Invalid component {output}")
|
|
308
|
-
return output
|
|
309
|
-
|
|
310
287
|
def __add__(self, other: "BuildGrain") -> "BuildGrain":
|
|
311
288
|
if not other:
|
|
312
289
|
return self
|
|
@@ -325,12 +302,12 @@ class BuildGrain(BaseModel):
|
|
|
325
302
|
# raise NotImplementedError(
|
|
326
303
|
# f"Cannot merge grains with where clauses, self {self.where_clause} other {other.where_clause}"
|
|
327
304
|
# )
|
|
328
|
-
return BuildGrain
|
|
305
|
+
return BuildGrain(
|
|
329
306
|
components=self.components.union(other.components), where_clause=where
|
|
330
307
|
)
|
|
331
308
|
|
|
332
309
|
def __sub__(self, other: "BuildGrain") -> "BuildGrain":
|
|
333
|
-
return BuildGrain
|
|
310
|
+
return BuildGrain(
|
|
334
311
|
components=self.components.difference(other.components),
|
|
335
312
|
where_clause=self.where_clause,
|
|
336
313
|
)
|
|
@@ -366,15 +343,35 @@ class BuildGrain(BaseModel):
|
|
|
366
343
|
intersection = self.components.intersection(other.components)
|
|
367
344
|
return BuildGrain(components=intersection)
|
|
368
345
|
|
|
369
|
-
def
|
|
346
|
+
def _calculate_string(self):
|
|
370
347
|
if self.abstract:
|
|
371
348
|
base = "Grain<Abstract>"
|
|
372
349
|
else:
|
|
373
|
-
base = "Grain<" + ",".join(
|
|
350
|
+
base = "Grain<" + ",".join(sorted(self.components)) + ">"
|
|
374
351
|
if self.where_clause:
|
|
375
352
|
base += f"|{str(self.where_clause)}"
|
|
376
353
|
return base
|
|
377
354
|
|
|
355
|
+
def _calculate_string_no_condition(self):
|
|
356
|
+
if self.abstract:
|
|
357
|
+
base = "Grain<Abstract>"
|
|
358
|
+
else:
|
|
359
|
+
base = "Grain<" + ",".join(sorted(self.components)) + ">"
|
|
360
|
+
return base
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
def str_no_condition(self):
|
|
364
|
+
if self._str_no_condition:
|
|
365
|
+
return self._str_no_condition
|
|
366
|
+
self._str_no_condition = self._calculate_string_no_condition()
|
|
367
|
+
return self._str_no_condition
|
|
368
|
+
|
|
369
|
+
def __str__(self):
|
|
370
|
+
if self._str:
|
|
371
|
+
return self._str
|
|
372
|
+
self._str = self._calculate_string()
|
|
373
|
+
return self._str
|
|
374
|
+
|
|
378
375
|
def __radd__(self, other) -> "BuildGrain":
|
|
379
376
|
if other == 0:
|
|
380
377
|
return self
|
|
@@ -382,7 +379,8 @@ class BuildGrain(BaseModel):
|
|
|
382
379
|
return self.__add__(other)
|
|
383
380
|
|
|
384
381
|
|
|
385
|
-
|
|
382
|
+
@dataclass
|
|
383
|
+
class BuildParenthetical(DataTyped, ConstantInlineable, BuildConceptArgs):
|
|
386
384
|
content: "BuildExpr"
|
|
387
385
|
|
|
388
386
|
def __add__(self, other) -> Union["BuildParenthetical", "BuildConditional"]:
|
|
@@ -436,7 +434,8 @@ class BuildParenthetical(DataTyped, ConstantInlineable, BuildConceptArgs, BaseMo
|
|
|
436
434
|
return arg_to_datatype(self.content)
|
|
437
435
|
|
|
438
436
|
|
|
439
|
-
|
|
437
|
+
@dataclass
|
|
438
|
+
class BuildConditional(BuildConceptArgs, ConstantInlineable):
|
|
440
439
|
left: Union[
|
|
441
440
|
int,
|
|
442
441
|
str,
|
|
@@ -564,7 +563,8 @@ class BuildConditional(BuildConceptArgs, ConstantInlineable, BaseModel):
|
|
|
564
563
|
return chunks
|
|
565
564
|
|
|
566
565
|
|
|
567
|
-
|
|
566
|
+
@dataclass
|
|
567
|
+
class BuildWhereClause(BuildConceptArgs):
|
|
568
568
|
conditional: Union[
|
|
569
569
|
BuildSubselectComparison,
|
|
570
570
|
BuildComparison,
|
|
@@ -600,7 +600,8 @@ class BuildHavingClause(BuildWhereClause):
|
|
|
600
600
|
pass
|
|
601
601
|
|
|
602
602
|
|
|
603
|
-
|
|
603
|
+
@dataclass
|
|
604
|
+
class BuildComparison(BuildConceptArgs, ConstantInlineable):
|
|
604
605
|
|
|
605
606
|
left: Union[
|
|
606
607
|
int,
|
|
@@ -730,6 +731,7 @@ class BuildComparison(BuildConceptArgs, ConstantInlineable, BaseModel):
|
|
|
730
731
|
return output
|
|
731
732
|
|
|
732
733
|
|
|
734
|
+
@dataclass
|
|
733
735
|
class BuildSubselectComparison(BuildComparison):
|
|
734
736
|
left: Union[
|
|
735
737
|
int,
|
|
@@ -791,18 +793,18 @@ class BuildSubselectComparison(BuildComparison):
|
|
|
791
793
|
return [tuple(get_concept_arguments(self.right))]
|
|
792
794
|
|
|
793
795
|
|
|
794
|
-
|
|
796
|
+
@dataclass
|
|
797
|
+
class BuildConcept(Addressable, BuildConceptArgs, DataTyped):
|
|
795
798
|
model_config = ConfigDict(extra="forbid")
|
|
796
799
|
name: str
|
|
797
|
-
datatype: DataType | ArrayType | StructType | MapType | NumericType
|
|
800
|
+
datatype: DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
|
|
798
801
|
purpose: Purpose
|
|
799
802
|
build_is_aggregate: bool
|
|
800
803
|
derivation: Derivation = Derivation.ROOT
|
|
801
804
|
granularity: Granularity = Granularity.MULTI_ROW
|
|
802
|
-
namespace: Optional[str] =
|
|
803
|
-
metadata: Metadata =
|
|
805
|
+
namespace: Optional[str] = field(default=DEFAULT_NAMESPACE)
|
|
806
|
+
metadata: Metadata = field(
|
|
804
807
|
default_factory=lambda: Metadata(description=None, line_number=None),
|
|
805
|
-
validate_default=True,
|
|
806
808
|
)
|
|
807
809
|
lineage: Optional[
|
|
808
810
|
Union[
|
|
@@ -816,22 +818,23 @@ class BuildConcept(Addressable, BuildConceptArgs, DataTyped, BaseModel):
|
|
|
816
818
|
] = None
|
|
817
819
|
|
|
818
820
|
keys: Optional[set[str]] = None
|
|
819
|
-
grain: BuildGrain =
|
|
820
|
-
modifiers: List[Modifier] =
|
|
821
|
-
pseudonyms: set[str] =
|
|
821
|
+
grain: BuildGrain = field(default=None) # type: ignore
|
|
822
|
+
modifiers: List[Modifier] = field(default_factory=list) # type: ignore
|
|
823
|
+
pseudonyms: set[str] = field(default_factory=set)
|
|
822
824
|
|
|
823
825
|
@property
|
|
824
826
|
def is_aggregate(self) -> bool:
|
|
825
827
|
return self.build_is_aggregate
|
|
826
828
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
def __hash__(self):
|
|
829
|
+
@cached_property
|
|
830
|
+
def hash(self) -> int:
|
|
831
831
|
return hash(
|
|
832
832
|
f"{self.name}+{self.datatype}+ {self.purpose} + {str(self.lineage)} + {self.namespace} + {str(self.grain)} + {str(self.keys)}"
|
|
833
833
|
)
|
|
834
834
|
|
|
835
|
+
def __hash__(self):
|
|
836
|
+
return self.hash
|
|
837
|
+
|
|
835
838
|
def __repr__(self):
|
|
836
839
|
base = f"{self.address}@{self.grain}"
|
|
837
840
|
return base
|
|
@@ -884,7 +887,7 @@ class BuildConcept(Addressable, BuildConceptArgs, DataTyped, BaseModel):
|
|
|
884
887
|
]
|
|
885
888
|
)
|
|
886
889
|
)
|
|
887
|
-
return self.__class__
|
|
890
|
+
return self.__class__(
|
|
888
891
|
name=self.name,
|
|
889
892
|
datatype=self.datatype,
|
|
890
893
|
purpose=self.purpose,
|
|
@@ -926,7 +929,7 @@ class BuildConcept(Addressable, BuildConceptArgs, DataTyped, BaseModel):
|
|
|
926
929
|
grain = self.grain
|
|
927
930
|
else:
|
|
928
931
|
grain = self.grain # type: ignore
|
|
929
|
-
return self.__class__
|
|
932
|
+
return self.__class__(
|
|
930
933
|
name=self.name,
|
|
931
934
|
datatype=self.datatype,
|
|
932
935
|
purpose=self.purpose,
|
|
@@ -982,7 +985,8 @@ class BuildConcept(Addressable, BuildConceptArgs, DataTyped, BaseModel):
|
|
|
982
985
|
return self.lineage.concept_arguments if self.lineage else []
|
|
983
986
|
|
|
984
987
|
|
|
985
|
-
|
|
988
|
+
@dataclass
|
|
989
|
+
class BuildOrderItem(DataTyped, BuildConceptArgs):
|
|
986
990
|
expr: BuildExpr
|
|
987
991
|
order: Ordering
|
|
988
992
|
|
|
@@ -1013,11 +1017,12 @@ class BuildOrderItem(DataTyped, BuildConceptArgs, BaseModel):
|
|
|
1013
1017
|
return arg_to_datatype(self.expr)
|
|
1014
1018
|
|
|
1015
1019
|
|
|
1016
|
-
|
|
1020
|
+
@dataclass
|
|
1021
|
+
class BuildWindowItem(DataTyped, BuildConceptArgs):
|
|
1017
1022
|
type: WindowType
|
|
1018
1023
|
content: BuildConcept
|
|
1019
1024
|
order_by: List[BuildOrderItem]
|
|
1020
|
-
over: List["BuildConcept"] =
|
|
1025
|
+
over: List["BuildConcept"] = field(default_factory=list)
|
|
1021
1026
|
index: Optional[int] = None
|
|
1022
1027
|
|
|
1023
1028
|
def __repr__(self) -> str:
|
|
@@ -1050,7 +1055,8 @@ class BuildWindowItem(DataTyped, BuildConceptArgs, BaseModel):
|
|
|
1050
1055
|
return Purpose.PROPERTY
|
|
1051
1056
|
|
|
1052
1057
|
|
|
1053
|
-
|
|
1058
|
+
@dataclass
|
|
1059
|
+
class BuildCaseWhen(BuildConceptArgs):
|
|
1054
1060
|
comparison: BuildConditional | BuildSubselectComparison | BuildComparison
|
|
1055
1061
|
expr: "BuildExpr"
|
|
1056
1062
|
|
|
@@ -1071,30 +1077,18 @@ class BuildCaseWhen(BuildConceptArgs, BaseModel):
|
|
|
1071
1077
|
)
|
|
1072
1078
|
|
|
1073
1079
|
|
|
1074
|
-
|
|
1080
|
+
@dataclass
|
|
1081
|
+
class BuildCaseElse(BuildConceptArgs):
|
|
1075
1082
|
expr: "BuildExpr"
|
|
1076
|
-
# this ensures that it's easily differentiable from CaseWhen
|
|
1077
|
-
discriminant: ComparisonOperator = ComparisonOperator.ELSE
|
|
1078
1083
|
|
|
1079
1084
|
@property
|
|
1080
1085
|
def concept_arguments(self):
|
|
1081
1086
|
return get_concept_arguments(self.expr)
|
|
1082
1087
|
|
|
1083
1088
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1089
|
+
@dataclass
|
|
1090
|
+
class BuildFunction(DataTyped, BuildConceptArgs):
|
|
1086
1091
|
operator: FunctionType
|
|
1087
|
-
arg_count: int = Field(default=1)
|
|
1088
|
-
output_datatype: (
|
|
1089
|
-
DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
|
|
1090
|
-
)
|
|
1091
|
-
output_purpose: Purpose
|
|
1092
|
-
valid_inputs: Optional[
|
|
1093
|
-
Union[
|
|
1094
|
-
Set[DataType],
|
|
1095
|
-
List[Set[DataType]],
|
|
1096
|
-
]
|
|
1097
|
-
] = None
|
|
1098
1092
|
arguments: Sequence[
|
|
1099
1093
|
Union[
|
|
1100
1094
|
int,
|
|
@@ -1120,6 +1114,17 @@ class BuildFunction(DataTyped, BuildConceptArgs, BaseModel):
|
|
|
1120
1114
|
ListWrapper[Any],
|
|
1121
1115
|
]
|
|
1122
1116
|
]
|
|
1117
|
+
output_data_type: (
|
|
1118
|
+
DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
|
|
1119
|
+
)
|
|
1120
|
+
output_purpose: Purpose = field(default=Purpose.KEY)
|
|
1121
|
+
arg_count: int = field(default=1)
|
|
1122
|
+
valid_inputs: Optional[
|
|
1123
|
+
Union[
|
|
1124
|
+
Set[DataType],
|
|
1125
|
+
List[Set[DataType]],
|
|
1126
|
+
]
|
|
1127
|
+
] = None
|
|
1123
1128
|
|
|
1124
1129
|
def __repr__(self):
|
|
1125
1130
|
return f'{self.operator.value}({",".join([str(a) for a in self.arguments])})'
|
|
@@ -1129,7 +1134,11 @@ class BuildFunction(DataTyped, BuildConceptArgs, BaseModel):
|
|
|
1129
1134
|
|
|
1130
1135
|
@property
|
|
1131
1136
|
def datatype(self):
|
|
1132
|
-
return self.
|
|
1137
|
+
return self.output_data_type
|
|
1138
|
+
|
|
1139
|
+
@property
|
|
1140
|
+
def output_datatype(self):
|
|
1141
|
+
return self.output_data_type
|
|
1133
1142
|
|
|
1134
1143
|
@property
|
|
1135
1144
|
def concept_arguments(self) -> List[BuildConcept]:
|
|
@@ -1141,18 +1150,19 @@ class BuildFunction(DataTyped, BuildConceptArgs, BaseModel):
|
|
|
1141
1150
|
@property
|
|
1142
1151
|
def output_grain(self):
|
|
1143
1152
|
# aggregates have an abstract grain
|
|
1144
|
-
base_grain = BuildGrain(components=[])
|
|
1145
1153
|
if self.operator in FunctionClass.AGGREGATE_FUNCTIONS.value:
|
|
1146
|
-
return
|
|
1154
|
+
return BuildGrain(components=[])
|
|
1147
1155
|
# scalars have implicit grain of all arguments
|
|
1156
|
+
args = set()
|
|
1148
1157
|
for input in self.concept_arguments:
|
|
1149
|
-
|
|
1150
|
-
return
|
|
1158
|
+
args += input.grain.components
|
|
1159
|
+
return BuildGrain(components=args)
|
|
1151
1160
|
|
|
1152
1161
|
|
|
1153
|
-
|
|
1162
|
+
@dataclass
|
|
1163
|
+
class BuildAggregateWrapper(BuildConceptArgs, DataTyped):
|
|
1154
1164
|
function: BuildFunction
|
|
1155
|
-
by: List[BuildConcept] =
|
|
1165
|
+
by: List[BuildConcept] = field(default_factory=list)
|
|
1156
1166
|
|
|
1157
1167
|
def __str__(self):
|
|
1158
1168
|
grain_str = [str(c) for c in self.by] if self.by else "abstract"
|
|
@@ -1175,7 +1185,8 @@ class BuildAggregateWrapper(BuildConceptArgs, DataTyped, BaseModel):
|
|
|
1175
1185
|
return self.function.output_purpose
|
|
1176
1186
|
|
|
1177
1187
|
|
|
1178
|
-
|
|
1188
|
+
@dataclass
|
|
1189
|
+
class BuildFilterItem(BuildConceptArgs):
|
|
1179
1190
|
content: "BuildExpr"
|
|
1180
1191
|
where: BuildWhereClause
|
|
1181
1192
|
|
|
@@ -1207,13 +1218,15 @@ class BuildFilterItem(BuildConceptArgs, BaseModel):
|
|
|
1207
1218
|
return self.where.concept_arguments
|
|
1208
1219
|
|
|
1209
1220
|
|
|
1210
|
-
|
|
1221
|
+
@dataclass
|
|
1222
|
+
class BuildRowsetLineage(BuildConceptArgs):
|
|
1211
1223
|
name: str
|
|
1212
1224
|
derived_concepts: List[str]
|
|
1213
1225
|
select: SelectLineage | MultiSelectLineage
|
|
1214
1226
|
|
|
1215
1227
|
|
|
1216
|
-
|
|
1228
|
+
@dataclass
|
|
1229
|
+
class BuildRowsetItem(DataTyped, BuildConceptArgs):
|
|
1217
1230
|
content: BuildConcept
|
|
1218
1231
|
rowset: BuildRowsetLineage
|
|
1219
1232
|
|
|
@@ -1240,7 +1253,8 @@ class BuildRowsetItem(DataTyped, BuildConceptArgs, BaseModel):
|
|
|
1240
1253
|
return [self.content]
|
|
1241
1254
|
|
|
1242
1255
|
|
|
1243
|
-
|
|
1256
|
+
@dataclass
|
|
1257
|
+
class BuildOrderBy:
|
|
1244
1258
|
items: List[BuildOrderItem]
|
|
1245
1259
|
|
|
1246
1260
|
@property
|
|
@@ -1248,39 +1262,42 @@ class BuildOrderBy(BaseModel):
|
|
|
1248
1262
|
return [x.expr for x in self.items]
|
|
1249
1263
|
|
|
1250
1264
|
|
|
1251
|
-
|
|
1265
|
+
@dataclass
|
|
1266
|
+
class BuildAlignClause:
|
|
1252
1267
|
items: List[BuildAlignItem]
|
|
1253
1268
|
|
|
1254
1269
|
|
|
1255
|
-
|
|
1270
|
+
@dataclass
|
|
1271
|
+
class BuildSelectLineage:
|
|
1256
1272
|
selection: List[BuildConcept]
|
|
1257
1273
|
hidden_components: set[str]
|
|
1258
1274
|
local_concepts: dict[str, BuildConcept]
|
|
1259
1275
|
order_by: Optional[BuildOrderBy] = None
|
|
1260
1276
|
limit: Optional[int] = None
|
|
1261
|
-
meta: Metadata =
|
|
1262
|
-
grain: BuildGrain =
|
|
1263
|
-
where_clause: BuildWhereClause | None =
|
|
1264
|
-
having_clause: BuildHavingClause | None =
|
|
1277
|
+
meta: Metadata = field(default_factory=lambda: Metadata())
|
|
1278
|
+
grain: BuildGrain = field(default_factory=BuildGrain)
|
|
1279
|
+
where_clause: BuildWhereClause | None = field(default=None)
|
|
1280
|
+
having_clause: BuildHavingClause | None = field(default=None)
|
|
1265
1281
|
|
|
1266
1282
|
@property
|
|
1267
1283
|
def output_components(self) -> List[BuildConcept]:
|
|
1268
1284
|
return self.selection
|
|
1269
1285
|
|
|
1270
1286
|
|
|
1271
|
-
|
|
1287
|
+
@dataclass
|
|
1288
|
+
class BuildMultiSelectLineage(BuildConceptArgs):
|
|
1272
1289
|
selects: List[SelectLineage]
|
|
1273
1290
|
grain: BuildGrain
|
|
1274
1291
|
align: BuildAlignClause
|
|
1275
1292
|
namespace: str
|
|
1276
|
-
order_by: Optional[BuildOrderBy] = None
|
|
1277
|
-
limit: Optional[int] = None
|
|
1278
|
-
where_clause: Union["BuildWhereClause", None] = Field(default=None)
|
|
1279
|
-
having_clause: Union["BuildHavingClause", None] = Field(default=None)
|
|
1280
1293
|
local_concepts: dict[str, BuildConcept]
|
|
1281
1294
|
build_concept_arguments: list[BuildConcept]
|
|
1282
1295
|
build_output_components: list[BuildConcept]
|
|
1283
1296
|
hidden_components: set[str]
|
|
1297
|
+
order_by: Optional[BuildOrderBy] = None
|
|
1298
|
+
limit: Optional[int] = None
|
|
1299
|
+
where_clause: Union["BuildWhereClause", None] = field(default=None)
|
|
1300
|
+
having_clause: Union["BuildHavingClause", None] = field(default=None)
|
|
1284
1301
|
|
|
1285
1302
|
@property
|
|
1286
1303
|
def derived_concepts(self) -> set[str]:
|
|
@@ -1322,12 +1339,12 @@ class BuildMultiSelectLineage(BuildConceptArgs, BaseModel):
|
|
|
1322
1339
|
)
|
|
1323
1340
|
|
|
1324
1341
|
|
|
1325
|
-
|
|
1342
|
+
@dataclass
|
|
1343
|
+
class BuildAlignItem:
|
|
1326
1344
|
alias: str
|
|
1327
1345
|
concepts: List[BuildConcept]
|
|
1328
|
-
namespace: str =
|
|
1346
|
+
namespace: str = field(default=DEFAULT_NAMESPACE)
|
|
1329
1347
|
|
|
1330
|
-
@computed_field # type: ignore
|
|
1331
1348
|
@cached_property
|
|
1332
1349
|
def concepts_lcl(self) -> LooseBuildConceptList:
|
|
1333
1350
|
return LooseBuildConceptList(concepts=self.concepts)
|
|
@@ -1337,10 +1354,11 @@ class BuildAlignItem(BaseModel):
|
|
|
1337
1354
|
return f"{self.namespace}.{self.alias}"
|
|
1338
1355
|
|
|
1339
1356
|
|
|
1340
|
-
|
|
1341
|
-
|
|
1357
|
+
@dataclass
|
|
1358
|
+
class BuildColumnAssignment:
|
|
1359
|
+
alias: str | RawColumnExpr | BuildFunction | BuildAggregateWrapper
|
|
1342
1360
|
concept: BuildConcept
|
|
1343
|
-
modifiers: List[Modifier] =
|
|
1361
|
+
modifiers: List[Modifier] = field(default_factory=list)
|
|
1344
1362
|
|
|
1345
1363
|
@property
|
|
1346
1364
|
def is_complete(self) -> bool:
|
|
@@ -1351,15 +1369,16 @@ class BuildColumnAssignment(BaseModel):
|
|
|
1351
1369
|
return Modifier.NULLABLE in self.modifiers
|
|
1352
1370
|
|
|
1353
1371
|
|
|
1354
|
-
|
|
1372
|
+
@dataclass
|
|
1373
|
+
class BuildDatasource:
|
|
1355
1374
|
name: str
|
|
1356
1375
|
columns: List[BuildColumnAssignment]
|
|
1357
1376
|
address: Union[Address, str]
|
|
1358
|
-
grain: BuildGrain =
|
|
1359
|
-
default_factory=lambda: BuildGrain(components=set()),
|
|
1377
|
+
grain: BuildGrain = field(
|
|
1378
|
+
default_factory=lambda: BuildGrain(components=set()),
|
|
1360
1379
|
)
|
|
1361
|
-
namespace: Optional[str] =
|
|
1362
|
-
metadata: DatasourceMetadata =
|
|
1380
|
+
namespace: Optional[str] = field(default=DEFAULT_NAMESPACE)
|
|
1381
|
+
metadata: DatasourceMetadata = field(
|
|
1363
1382
|
default_factory=lambda: DatasourceMetadata(freshness_concept=None)
|
|
1364
1383
|
)
|
|
1365
1384
|
where: Optional[BuildWhereClause] = None
|
|
@@ -1428,7 +1447,7 @@ class BuildDatasource(BaseModel):
|
|
|
1428
1447
|
concept: BuildConcept,
|
|
1429
1448
|
use_raw_name: bool = True,
|
|
1430
1449
|
force_alias: bool = False,
|
|
1431
|
-
) -> Optional[str | RawColumnExpr] | BuildFunction:
|
|
1450
|
+
) -> Optional[str | RawColumnExpr] | BuildFunction | BuildAggregateWrapper:
|
|
1432
1451
|
# 2022-01-22
|
|
1433
1452
|
# this logic needs to be refined.
|
|
1434
1453
|
# if concept.lineage:
|
|
@@ -1472,8 +1491,6 @@ BuildExpr = (
|
|
|
1472
1491
|
| list
|
|
1473
1492
|
)
|
|
1474
1493
|
|
|
1475
|
-
BuildConcept.model_rebuild()
|
|
1476
|
-
|
|
1477
1494
|
|
|
1478
1495
|
def get_canonical_pseudonyms(environment: Environment) -> dict[str, set[str]]:
|
|
1479
1496
|
roots: dict[str, set[str]] = defaultdict(set)
|
|
@@ -1507,13 +1524,15 @@ class Factory:
|
|
|
1507
1524
|
environment: Environment,
|
|
1508
1525
|
local_concepts: dict[str, BuildConcept] | None = None,
|
|
1509
1526
|
grain: Grain | None = None,
|
|
1527
|
+
pseudonym_map: dict[str, set[str]] | None = None,
|
|
1510
1528
|
):
|
|
1511
1529
|
self.grain = grain or Grain()
|
|
1512
1530
|
self.environment = environment
|
|
1513
1531
|
self.local_concepts: dict[str, BuildConcept] = (
|
|
1514
1532
|
{} if local_concepts is None else local_concepts
|
|
1515
1533
|
)
|
|
1516
|
-
self.
|
|
1534
|
+
self.local_non_build_concepts: dict[str, Concept] = {}
|
|
1535
|
+
self.pseudonym_map = pseudonym_map or get_canonical_pseudonyms(environment)
|
|
1517
1536
|
|
|
1518
1537
|
def instantiate_concept(
|
|
1519
1538
|
self,
|
|
@@ -1531,14 +1550,19 @@ class Factory:
|
|
|
1531
1550
|
| date
|
|
1532
1551
|
),
|
|
1533
1552
|
) -> tuple[Concept, BuildConcept]:
|
|
1534
|
-
from trilogy.parsing.common import arbitrary_to_concept
|
|
1553
|
+
from trilogy.parsing.common import arbitrary_to_concept, generate_concept_name
|
|
1535
1554
|
|
|
1555
|
+
name = generate_concept_name(arg)
|
|
1556
|
+
if name in self.local_concepts and name in self.local_non_build_concepts:
|
|
1557
|
+
# if we already have this concept, return it
|
|
1558
|
+
return self.local_non_build_concepts[name], self.local_concepts[name]
|
|
1536
1559
|
new = arbitrary_to_concept(
|
|
1537
1560
|
arg,
|
|
1538
1561
|
environment=self.environment,
|
|
1539
1562
|
)
|
|
1540
|
-
built = self.
|
|
1541
|
-
self.local_concepts[
|
|
1563
|
+
built = self._build_concept(new)
|
|
1564
|
+
self.local_concepts[name] = built
|
|
1565
|
+
self.local_non_build_concepts[name] = new
|
|
1542
1566
|
return new, built
|
|
1543
1567
|
|
|
1544
1568
|
@singledispatchmethod
|
|
@@ -1576,15 +1600,23 @@ class Factory:
|
|
|
1576
1600
|
| DatePart
|
|
1577
1601
|
| NumericType
|
|
1578
1602
|
):
|
|
1603
|
+
return self._build_primitive(base)
|
|
1604
|
+
|
|
1605
|
+
def _build_primitive(self, base):
|
|
1579
1606
|
return base
|
|
1580
1607
|
|
|
1581
1608
|
@build.register
|
|
1582
1609
|
def _(self, base: None) -> None:
|
|
1610
|
+
return self._build_none(base)
|
|
1611
|
+
|
|
1612
|
+
def _build_none(self, base):
|
|
1583
1613
|
return base
|
|
1584
1614
|
|
|
1585
1615
|
@build.register
|
|
1586
1616
|
def _(self, base: Function) -> BuildFunction | BuildAggregateWrapper:
|
|
1617
|
+
return self._build_function(base)
|
|
1587
1618
|
|
|
1619
|
+
def _build_function(self, base: Function) -> BuildFunction | BuildAggregateWrapper:
|
|
1588
1620
|
raw_args: list[Concept | FuncArgs] = []
|
|
1589
1621
|
for arg in base.arguments:
|
|
1590
1622
|
# to do proper discovery, we need to inject virtual intermediate concepts
|
|
@@ -1621,22 +1653,22 @@ class Factory:
|
|
|
1621
1653
|
)
|
|
1622
1654
|
)
|
|
1623
1655
|
|
|
1624
|
-
return BuildFunction
|
|
1656
|
+
return BuildFunction(
|
|
1625
1657
|
operator=base.operator,
|
|
1626
1658
|
arguments=[
|
|
1627
1659
|
rval,
|
|
1628
1660
|
*[self.handle_constant(self.build(c)) for c in raw_args[1:]],
|
|
1629
1661
|
],
|
|
1630
|
-
|
|
1662
|
+
output_data_type=base.output_datatype,
|
|
1631
1663
|
output_purpose=base.output_purpose,
|
|
1632
1664
|
valid_inputs=base.valid_inputs,
|
|
1633
1665
|
arg_count=base.arg_count,
|
|
1634
1666
|
)
|
|
1635
1667
|
|
|
1636
|
-
new = BuildFunction
|
|
1668
|
+
new = BuildFunction(
|
|
1637
1669
|
operator=base.operator,
|
|
1638
1670
|
arguments=[self.handle_constant(self.build(c)) for c in raw_args],
|
|
1639
|
-
|
|
1671
|
+
output_data_type=base.output_datatype,
|
|
1640
1672
|
output_purpose=base.output_purpose,
|
|
1641
1673
|
valid_inputs=base.valid_inputs,
|
|
1642
1674
|
arg_count=base.arg_count,
|
|
@@ -1645,40 +1677,50 @@ class Factory:
|
|
|
1645
1677
|
|
|
1646
1678
|
@build.register
|
|
1647
1679
|
def _(self, base: ConceptRef) -> BuildConcept:
|
|
1680
|
+
return self._build_concept_ref(base)
|
|
1681
|
+
|
|
1682
|
+
def _build_concept_ref(self, base: ConceptRef) -> BuildConcept:
|
|
1648
1683
|
if base.address in self.local_concepts:
|
|
1649
1684
|
full = self.local_concepts[base.address]
|
|
1650
1685
|
if isinstance(full, BuildConcept):
|
|
1651
1686
|
return full
|
|
1652
1687
|
if base.address in self.environment.concepts:
|
|
1653
1688
|
raw = self.environment.concepts[base.address]
|
|
1654
|
-
return self.
|
|
1689
|
+
return self._build_concept(raw)
|
|
1655
1690
|
# this will error by design - TODO - more helpful message?
|
|
1656
|
-
return self.
|
|
1691
|
+
return self._build_concept(self.environment.concepts[base.address])
|
|
1657
1692
|
|
|
1658
1693
|
@build.register
|
|
1659
1694
|
def _(self, base: CaseWhen) -> BuildCaseWhen:
|
|
1695
|
+
return self._build_case_when(base)
|
|
1660
1696
|
|
|
1697
|
+
def _build_case_when(self, base: CaseWhen) -> BuildCaseWhen:
|
|
1661
1698
|
comparison = base.comparison
|
|
1662
1699
|
expr: Concept | FuncArgs = base.expr
|
|
1663
1700
|
validation = requires_concept_nesting(expr)
|
|
1664
1701
|
if validation:
|
|
1665
1702
|
expr, _ = self.instantiate_concept(validation)
|
|
1666
|
-
return BuildCaseWhen
|
|
1703
|
+
return BuildCaseWhen(
|
|
1667
1704
|
comparison=self.build(comparison),
|
|
1668
1705
|
expr=self.build(expr),
|
|
1669
1706
|
)
|
|
1670
1707
|
|
|
1671
1708
|
@build.register
|
|
1672
1709
|
def _(self, base: CaseElse) -> BuildCaseElse:
|
|
1710
|
+
return self._build_case_else(base)
|
|
1711
|
+
|
|
1712
|
+
def _build_case_else(self, base: CaseElse) -> BuildCaseElse:
|
|
1673
1713
|
expr: Concept | FuncArgs = base.expr
|
|
1674
1714
|
validation = requires_concept_nesting(expr)
|
|
1675
1715
|
if validation:
|
|
1676
1716
|
expr, _ = self.instantiate_concept(validation)
|
|
1677
|
-
return BuildCaseElse
|
|
1717
|
+
return BuildCaseElse(expr=self.build(expr))
|
|
1678
1718
|
|
|
1679
1719
|
@build.register
|
|
1680
1720
|
def _(self, base: Concept) -> BuildConcept:
|
|
1721
|
+
return self._build_concept(base)
|
|
1681
1722
|
|
|
1723
|
+
def _build_concept(self, base: Concept) -> BuildConcept:
|
|
1682
1724
|
# TODO: if we are using parameters, wrap it in a new model and use that in rendering
|
|
1683
1725
|
if base.address in self.local_concepts:
|
|
1684
1726
|
return self.local_concepts[base.address]
|
|
@@ -1707,13 +1749,13 @@ class Factory:
|
|
|
1707
1749
|
for x in self.pseudonym_map.get(base.address, set())
|
|
1708
1750
|
if x != base.address
|
|
1709
1751
|
}
|
|
1710
|
-
rval = BuildConcept
|
|
1752
|
+
rval = BuildConcept(
|
|
1711
1753
|
name=base.name,
|
|
1712
1754
|
datatype=base.datatype,
|
|
1713
1755
|
purpose=base.purpose,
|
|
1714
1756
|
metadata=base.metadata,
|
|
1715
1757
|
lineage=build_lineage,
|
|
1716
|
-
grain=self.
|
|
1758
|
+
grain=self._build_grain(final_grain),
|
|
1717
1759
|
namespace=base.namespace,
|
|
1718
1760
|
keys=base.keys,
|
|
1719
1761
|
modifiers=base.modifiers,
|
|
@@ -1723,34 +1765,44 @@ class Factory:
|
|
|
1723
1765
|
granularity=granularity,
|
|
1724
1766
|
build_is_aggregate=is_aggregate,
|
|
1725
1767
|
)
|
|
1768
|
+
self.local_concepts[base.address] = rval
|
|
1726
1769
|
return rval
|
|
1727
1770
|
|
|
1728
1771
|
@build.register
|
|
1729
1772
|
def _(self, base: AggregateWrapper) -> BuildAggregateWrapper:
|
|
1773
|
+
return self._build_aggregate_wrapper(base)
|
|
1774
|
+
|
|
1775
|
+
def _build_aggregate_wrapper(self, base: AggregateWrapper) -> BuildAggregateWrapper:
|
|
1730
1776
|
if not base.by:
|
|
1731
1777
|
by = [
|
|
1732
|
-
self.
|
|
1778
|
+
self._build_concept(self.environment.concepts[c])
|
|
1779
|
+
for c in self.grain.components
|
|
1733
1780
|
]
|
|
1734
1781
|
else:
|
|
1735
1782
|
by = [self.build(x) for x in base.by]
|
|
1736
1783
|
|
|
1737
|
-
parent = self.
|
|
1738
|
-
return BuildAggregateWrapper
|
|
1784
|
+
parent: BuildFunction = self._build_function(base.function) # type: ignore
|
|
1785
|
+
return BuildAggregateWrapper(function=parent, by=by)
|
|
1739
1786
|
|
|
1740
1787
|
@build.register
|
|
1741
1788
|
def _(self, base: ColumnAssignment) -> BuildColumnAssignment:
|
|
1789
|
+
return self._build_column_assignment(base)
|
|
1790
|
+
|
|
1791
|
+
def _build_column_assignment(self, base: ColumnAssignment) -> BuildColumnAssignment:
|
|
1742
1792
|
address = base.concept.address
|
|
1743
1793
|
fetched = (
|
|
1744
|
-
self.
|
|
1794
|
+
self._build_concept(
|
|
1745
1795
|
self.environment.alias_origin_lookup[address].with_grain(self.grain)
|
|
1746
1796
|
)
|
|
1747
1797
|
if address in self.environment.alias_origin_lookup
|
|
1748
|
-
else self.
|
|
1798
|
+
else self._build_concept(
|
|
1799
|
+
self.environment.concepts[address].with_grain(self.grain)
|
|
1800
|
+
)
|
|
1749
1801
|
)
|
|
1750
1802
|
|
|
1751
|
-
return BuildColumnAssignment
|
|
1803
|
+
return BuildColumnAssignment(
|
|
1752
1804
|
alias=(
|
|
1753
|
-
self.
|
|
1805
|
+
self._build_function(base.alias)
|
|
1754
1806
|
if isinstance(base.alias, Function)
|
|
1755
1807
|
else base.alias
|
|
1756
1808
|
),
|
|
@@ -1760,44 +1812,55 @@ class Factory:
|
|
|
1760
1812
|
|
|
1761
1813
|
@build.register
|
|
1762
1814
|
def _(self, base: OrderBy) -> BuildOrderBy:
|
|
1763
|
-
return
|
|
1815
|
+
return self._build_order_by(base)
|
|
1816
|
+
|
|
1817
|
+
def _build_order_by(self, base: OrderBy) -> BuildOrderBy:
|
|
1818
|
+
return BuildOrderBy(items=[self._build_order_item(x) for x in base.items])
|
|
1764
1819
|
|
|
1765
1820
|
@build.register
|
|
1766
1821
|
def _(self, base: FunctionCallWrapper) -> BuildExpr:
|
|
1822
|
+
return self._build_function_call_wrapper(base)
|
|
1823
|
+
|
|
1824
|
+
def _build_function_call_wrapper(self, base: FunctionCallWrapper) -> BuildExpr:
|
|
1767
1825
|
# function calls are kept around purely for the parse tree
|
|
1768
1826
|
# so discard at the build point
|
|
1769
1827
|
return self.build(base.content)
|
|
1770
1828
|
|
|
1771
1829
|
@build.register
|
|
1772
1830
|
def _(self, base: OrderItem) -> BuildOrderItem:
|
|
1831
|
+
return self._build_order_item(base)
|
|
1773
1832
|
|
|
1833
|
+
def _build_order_item(self, base: OrderItem) -> BuildOrderItem:
|
|
1774
1834
|
bexpr: Any
|
|
1775
1835
|
validation = requires_concept_nesting(base.expr)
|
|
1776
1836
|
if validation:
|
|
1777
1837
|
bexpr, _ = self.instantiate_concept(validation)
|
|
1778
1838
|
else:
|
|
1779
1839
|
bexpr = base.expr
|
|
1780
|
-
return BuildOrderItem
|
|
1840
|
+
return BuildOrderItem(
|
|
1781
1841
|
expr=(self.build(bexpr)),
|
|
1782
1842
|
order=base.order,
|
|
1783
1843
|
)
|
|
1784
1844
|
|
|
1785
1845
|
@build.register
|
|
1786
1846
|
def _(self, base: WhereClause) -> BuildWhereClause:
|
|
1847
|
+
return self._build_where_clause(base)
|
|
1787
1848
|
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
)
|
|
1849
|
+
def _build_where_clause(self, base: WhereClause) -> BuildWhereClause:
|
|
1850
|
+
return BuildWhereClause(conditional=self.build(base.conditional))
|
|
1791
1851
|
|
|
1792
1852
|
@build.register
|
|
1793
1853
|
def _(self, base: HavingClause) -> BuildHavingClause:
|
|
1794
|
-
return
|
|
1795
|
-
|
|
1796
|
-
|
|
1854
|
+
return self._build_having_clause(base)
|
|
1855
|
+
|
|
1856
|
+
def _build_having_clause(self, base: HavingClause) -> BuildHavingClause:
|
|
1857
|
+
return BuildHavingClause(conditional=self.build(base.conditional))
|
|
1797
1858
|
|
|
1798
1859
|
@build.register
|
|
1799
1860
|
def _(self, base: WindowItem) -> BuildWindowItem:
|
|
1861
|
+
return self._build_window_item(base)
|
|
1800
1862
|
|
|
1863
|
+
def _build_window_item(self, base: WindowItem) -> BuildWindowItem:
|
|
1801
1864
|
content: Concept | FuncArgs = base.content
|
|
1802
1865
|
validation = requires_concept_nesting(base.content)
|
|
1803
1866
|
if validation:
|
|
@@ -1811,17 +1874,20 @@ class Factory:
|
|
|
1811
1874
|
):
|
|
1812
1875
|
x.expr.by = [content]
|
|
1813
1876
|
final_by.append(x)
|
|
1814
|
-
return BuildWindowItem
|
|
1877
|
+
return BuildWindowItem(
|
|
1815
1878
|
type=base.type,
|
|
1816
1879
|
content=self.build(content),
|
|
1817
1880
|
order_by=[self.build(x) for x in final_by],
|
|
1818
|
-
over=[self.
|
|
1881
|
+
over=[self._build_concept_ref(x) for x in base.over],
|
|
1819
1882
|
index=base.index,
|
|
1820
1883
|
)
|
|
1821
1884
|
|
|
1822
1885
|
@build.register
|
|
1823
1886
|
def _(self, base: Conditional) -> BuildConditional:
|
|
1824
|
-
return
|
|
1887
|
+
return self._build_conditional(base)
|
|
1888
|
+
|
|
1889
|
+
def _build_conditional(self, base: Conditional) -> BuildConditional:
|
|
1890
|
+
return BuildConditional(
|
|
1825
1891
|
left=self.handle_constant(self.build(base.left)),
|
|
1826
1892
|
right=self.handle_constant(self.build(base.right)),
|
|
1827
1893
|
operator=base.operator,
|
|
@@ -1829,12 +1895,17 @@ class Factory:
|
|
|
1829
1895
|
|
|
1830
1896
|
@build.register
|
|
1831
1897
|
def _(self, base: SubselectComparison) -> BuildSubselectComparison:
|
|
1898
|
+
return self._build_subselect_comparison(base)
|
|
1899
|
+
|
|
1900
|
+
def _build_subselect_comparison(
|
|
1901
|
+
self, base: SubselectComparison
|
|
1902
|
+
) -> BuildSubselectComparison:
|
|
1832
1903
|
right: Any = base.right
|
|
1833
1904
|
# this has specialized logic - include all Functions
|
|
1834
1905
|
if isinstance(base.right, (AggregateWrapper, WindowItem, FilterItem, Function)):
|
|
1835
1906
|
right_c, _ = self.instantiate_concept(base.right)
|
|
1836
1907
|
right = right_c
|
|
1837
|
-
return BuildSubselectComparison
|
|
1908
|
+
return BuildSubselectComparison(
|
|
1838
1909
|
left=self.handle_constant(self.build(base.left)),
|
|
1839
1910
|
right=self.handle_constant(self.build(right)),
|
|
1840
1911
|
operator=base.operator,
|
|
@@ -1842,7 +1913,9 @@ class Factory:
|
|
|
1842
1913
|
|
|
1843
1914
|
@build.register
|
|
1844
1915
|
def _(self, base: Comparison) -> BuildComparison:
|
|
1916
|
+
return self._build_comparison(base)
|
|
1845
1917
|
|
|
1918
|
+
def _build_comparison(self, base: Comparison) -> BuildComparison:
|
|
1846
1919
|
left = base.left
|
|
1847
1920
|
validation = requires_concept_nesting(base.left)
|
|
1848
1921
|
if validation:
|
|
@@ -1853,7 +1926,7 @@ class Factory:
|
|
|
1853
1926
|
if validation:
|
|
1854
1927
|
right_c, _ = self.instantiate_concept(validation)
|
|
1855
1928
|
right = right_c # type: ignore
|
|
1856
|
-
return BuildComparison
|
|
1929
|
+
return BuildComparison(
|
|
1857
1930
|
left=self.handle_constant(self.build(left)),
|
|
1858
1931
|
right=self.handle_constant(self.build(right)),
|
|
1859
1932
|
operator=base.operator,
|
|
@@ -1861,32 +1934,43 @@ class Factory:
|
|
|
1861
1934
|
|
|
1862
1935
|
@build.register
|
|
1863
1936
|
def _(self, base: AlignItem) -> BuildAlignItem:
|
|
1864
|
-
return
|
|
1937
|
+
return self._build_align_item(base)
|
|
1938
|
+
|
|
1939
|
+
def _build_align_item(self, base: AlignItem) -> BuildAlignItem:
|
|
1940
|
+
return BuildAlignItem(
|
|
1865
1941
|
alias=base.alias,
|
|
1866
|
-
concepts=[self.
|
|
1942
|
+
concepts=[self._build_concept_ref(x) for x in base.concepts],
|
|
1867
1943
|
namespace=base.namespace,
|
|
1868
1944
|
)
|
|
1869
1945
|
|
|
1870
1946
|
@build.register
|
|
1871
1947
|
def _(self, base: AlignClause) -> BuildAlignClause:
|
|
1872
|
-
return
|
|
1873
|
-
|
|
1874
|
-
|
|
1948
|
+
return self._build_align_clause(base)
|
|
1949
|
+
|
|
1950
|
+
def _build_align_clause(self, base: AlignClause) -> BuildAlignClause:
|
|
1951
|
+
return BuildAlignClause(items=[self._build_align_item(x) for x in base.items])
|
|
1875
1952
|
|
|
1876
1953
|
@build.register
|
|
1877
1954
|
def _(self, base: RowsetItem) -> BuildRowsetItem:
|
|
1955
|
+
return self._build_rowset_item(base)
|
|
1878
1956
|
|
|
1957
|
+
def _build_rowset_item(self, base: RowsetItem) -> BuildRowsetItem:
|
|
1879
1958
|
factory = Factory(
|
|
1880
1959
|
environment=self.environment,
|
|
1881
1960
|
local_concepts={},
|
|
1882
1961
|
grain=base.rowset.select.grain,
|
|
1962
|
+
pseudonym_map=self.pseudonym_map,
|
|
1883
1963
|
)
|
|
1884
1964
|
return BuildRowsetItem(
|
|
1885
|
-
content=factory.
|
|
1965
|
+
content=factory._build_concept_ref(base.content),
|
|
1966
|
+
rowset=factory._build_rowset_lineage(base.rowset),
|
|
1886
1967
|
)
|
|
1887
1968
|
|
|
1888
1969
|
@build.register
|
|
1889
1970
|
def _(self, base: RowsetLineage) -> BuildRowsetLineage:
|
|
1971
|
+
return self._build_rowset_lineage(base)
|
|
1972
|
+
|
|
1973
|
+
def _build_rowset_lineage(self, base: RowsetLineage) -> BuildRowsetLineage:
|
|
1890
1974
|
out = BuildRowsetLineage(
|
|
1891
1975
|
name=base.name,
|
|
1892
1976
|
derived_concepts=[x.address for x in base.derived_concepts],
|
|
@@ -1896,39 +1980,51 @@ class Factory:
|
|
|
1896
1980
|
|
|
1897
1981
|
@build.register
|
|
1898
1982
|
def _(self, base: Grain) -> BuildGrain:
|
|
1983
|
+
return self._build_grain(base)
|
|
1984
|
+
|
|
1985
|
+
def _build_grain(self, base: Grain) -> BuildGrain:
|
|
1899
1986
|
if base.where_clause:
|
|
1900
|
-
factory = Factory(
|
|
1901
|
-
|
|
1987
|
+
factory = Factory(
|
|
1988
|
+
environment=self.environment, pseudonym_map=self.pseudonym_map
|
|
1989
|
+
)
|
|
1990
|
+
where = factory._build_where_clause(base.where_clause)
|
|
1902
1991
|
else:
|
|
1903
1992
|
where = None
|
|
1904
|
-
return BuildGrain.
|
|
1905
|
-
components=base.components, where_clause=where
|
|
1906
|
-
)
|
|
1993
|
+
return BuildGrain(components=base.components, where_clause=where)
|
|
1907
1994
|
|
|
1908
1995
|
@build.register
|
|
1909
1996
|
def _(self, base: TupleWrapper) -> TupleWrapper:
|
|
1997
|
+
return self._build_tuple_wrapper(base)
|
|
1998
|
+
|
|
1999
|
+
def _build_tuple_wrapper(self, base: TupleWrapper) -> TupleWrapper:
|
|
1910
2000
|
return TupleWrapper(val=[self.build(x) for x in base.val], type=base.type)
|
|
1911
2001
|
|
|
1912
2002
|
@build.register
|
|
1913
2003
|
def _(self, base: FilterItem) -> BuildFilterItem:
|
|
2004
|
+
return self._build_filter_item(base)
|
|
2005
|
+
|
|
2006
|
+
def _build_filter_item(self, base: FilterItem) -> BuildFilterItem:
|
|
1914
2007
|
if isinstance(
|
|
1915
2008
|
base.content, (Function, AggregateWrapper, WindowItem, FilterItem)
|
|
1916
2009
|
):
|
|
1917
2010
|
_, built = self.instantiate_concept(base.content)
|
|
1918
|
-
return BuildFilterItem.
|
|
1919
|
-
|
|
1920
|
-
)
|
|
1921
|
-
return BuildFilterItem.model_construct(
|
|
2011
|
+
return BuildFilterItem(content=built, where=self.build(base.where))
|
|
2012
|
+
return BuildFilterItem(
|
|
1922
2013
|
content=self.build(base.content), where=self.build(base.where)
|
|
1923
2014
|
)
|
|
1924
2015
|
|
|
1925
2016
|
@build.register
|
|
1926
2017
|
def _(self, base: Parenthetical) -> BuildParenthetical:
|
|
1927
|
-
return
|
|
2018
|
+
return self._build_parenthetical(base)
|
|
2019
|
+
|
|
2020
|
+
def _build_parenthetical(self, base: Parenthetical) -> BuildParenthetical:
|
|
2021
|
+
return BuildParenthetical(content=(self.build(base.content)))
|
|
1928
2022
|
|
|
1929
2023
|
@build.register
|
|
1930
2024
|
def _(self, base: SelectLineage) -> BuildSelectLineage:
|
|
2025
|
+
return self._build_select_lineage(base)
|
|
1931
2026
|
|
|
2027
|
+
def _build_select_lineage(self, base: SelectLineage) -> BuildSelectLineage:
|
|
1932
2028
|
from trilogy.core.models.build import (
|
|
1933
2029
|
BuildSelectLineage,
|
|
1934
2030
|
Factory,
|
|
@@ -1936,12 +2032,18 @@ class Factory:
|
|
|
1936
2032
|
|
|
1937
2033
|
materialized: dict[str, BuildConcept] = {}
|
|
1938
2034
|
factory = Factory(
|
|
1939
|
-
grain=base.grain,
|
|
2035
|
+
grain=base.grain,
|
|
2036
|
+
environment=self.environment,
|
|
2037
|
+
local_concepts=materialized,
|
|
2038
|
+
pseudonym_map=self.pseudonym_map,
|
|
1940
2039
|
)
|
|
1941
2040
|
for k, v in base.local_concepts.items():
|
|
1942
2041
|
materialized[k] = factory.build(v)
|
|
1943
2042
|
where_factory = Factory(
|
|
1944
|
-
grain=Grain(),
|
|
2043
|
+
grain=Grain(),
|
|
2044
|
+
environment=self.environment,
|
|
2045
|
+
local_concepts={},
|
|
2046
|
+
pseudonym_map=self.pseudonym_map,
|
|
1945
2047
|
)
|
|
1946
2048
|
where_clause = (
|
|
1947
2049
|
where_factory.build(base.where_clause) if base.where_clause else None
|
|
@@ -1985,7 +2087,11 @@ class Factory:
|
|
|
1985
2087
|
|
|
1986
2088
|
@build.register
|
|
1987
2089
|
def _(self, base: MultiSelectLineage) -> BuildMultiSelectLineage:
|
|
2090
|
+
return self._build_multi_select_lineage(base)
|
|
1988
2091
|
|
|
2092
|
+
def _build_multi_select_lineage(
|
|
2093
|
+
self, base: MultiSelectLineage
|
|
2094
|
+
) -> BuildMultiSelectLineage:
|
|
1989
2095
|
local_build_cache: dict[str, BuildConcept] = {}
|
|
1990
2096
|
|
|
1991
2097
|
parents: list[BuildSelectLineage] = [self.build(x) for x in base.selects]
|
|
@@ -2000,7 +2106,7 @@ class Factory:
|
|
|
2000
2106
|
derived_base = []
|
|
2001
2107
|
for k in base.derived_concepts:
|
|
2002
2108
|
base_concept = self.environment.concepts[k]
|
|
2003
|
-
x = BuildConcept
|
|
2109
|
+
x = BuildConcept(
|
|
2004
2110
|
name=base_concept.name,
|
|
2005
2111
|
datatype=base_concept.datatype,
|
|
2006
2112
|
purpose=base_concept.purpose,
|
|
@@ -2023,9 +2129,12 @@ class Factory:
|
|
|
2023
2129
|
grain=base.grain,
|
|
2024
2130
|
environment=self.environment,
|
|
2025
2131
|
local_concepts=local_build_cache,
|
|
2132
|
+
pseudonym_map=self.pseudonym_map,
|
|
2026
2133
|
)
|
|
2027
|
-
where_factory = Factory(
|
|
2028
|
-
|
|
2134
|
+
where_factory = Factory(
|
|
2135
|
+
environment=self.environment, pseudonym_map=self.pseudonym_map
|
|
2136
|
+
)
|
|
2137
|
+
lineage = BuildMultiSelectLineage(
|
|
2029
2138
|
# we don't build selects here; they'll be built automatically in query discovery
|
|
2030
2139
|
selects=base.selects,
|
|
2031
2140
|
grain=final_grain,
|
|
@@ -2053,6 +2162,9 @@ class Factory:
|
|
|
2053
2162
|
|
|
2054
2163
|
@build.register
|
|
2055
2164
|
def _(self, base: Environment):
|
|
2165
|
+
return self._build_environment(base)
|
|
2166
|
+
|
|
2167
|
+
def _build_environment(self, base: Environment):
|
|
2056
2168
|
from trilogy.core.models.build_environment import BuildEnvironment
|
|
2057
2169
|
|
|
2058
2170
|
new = BuildEnvironment(
|
|
@@ -2061,14 +2173,14 @@ class Factory:
|
|
|
2061
2173
|
)
|
|
2062
2174
|
|
|
2063
2175
|
for k, v in base.concepts.items():
|
|
2064
|
-
new.concepts[k] = self.
|
|
2176
|
+
new.concepts[k] = self._build_concept(v)
|
|
2065
2177
|
for (
|
|
2066
2178
|
k,
|
|
2067
2179
|
d,
|
|
2068
2180
|
) in base.datasources.items():
|
|
2069
|
-
new.datasources[k] = self.
|
|
2181
|
+
new.datasources[k] = self._build_datasource(d)
|
|
2070
2182
|
for k, a in base.alias_origin_lookup.items():
|
|
2071
|
-
new.alias_origin_lookup[k] = self.
|
|
2183
|
+
new.alias_origin_lookup[k] = self._build_concept(a)
|
|
2072
2184
|
# add in anything that was built as a side-effect
|
|
2073
2185
|
for bk, bv in self.local_concepts.items():
|
|
2074
2186
|
if bk not in new.concepts:
|
|
@@ -2078,39 +2190,63 @@ class Factory:
|
|
|
2078
2190
|
|
|
2079
2191
|
@build.register
|
|
2080
2192
|
def _(self, base: TraitDataType):
|
|
2193
|
+
return self._build_trait_data_type(base)
|
|
2194
|
+
|
|
2195
|
+
def _build_trait_data_type(self, base: TraitDataType):
|
|
2081
2196
|
return base
|
|
2082
2197
|
|
|
2083
2198
|
@build.register
|
|
2084
2199
|
def _(self, base: ArrayType):
|
|
2200
|
+
return self._build_array_type(base)
|
|
2201
|
+
|
|
2202
|
+
def _build_array_type(self, base: ArrayType):
|
|
2085
2203
|
return base
|
|
2086
2204
|
|
|
2087
2205
|
@build.register
|
|
2088
2206
|
def _(self, base: StructType):
|
|
2207
|
+
return self._build_struct_type(base)
|
|
2208
|
+
|
|
2209
|
+
def _build_struct_type(self, base: StructType):
|
|
2089
2210
|
return base
|
|
2090
2211
|
|
|
2091
2212
|
@build.register
|
|
2092
2213
|
def _(self, base: MapType):
|
|
2214
|
+
return self._build_map_type(base)
|
|
2215
|
+
|
|
2216
|
+
def _build_map_type(self, base: MapType):
|
|
2093
2217
|
return base
|
|
2094
2218
|
|
|
2095
2219
|
@build.register
|
|
2096
2220
|
def _(self, base: ArgBinding):
|
|
2221
|
+
return self._build_arg_binding(base)
|
|
2222
|
+
|
|
2223
|
+
def _build_arg_binding(self, base: ArgBinding):
|
|
2097
2224
|
return base
|
|
2098
2225
|
|
|
2099
2226
|
@build.register
|
|
2100
2227
|
def _(self, base: Ordering):
|
|
2228
|
+
return self._build_ordering(base)
|
|
2229
|
+
|
|
2230
|
+
def _build_ordering(self, base: Ordering):
|
|
2101
2231
|
return base
|
|
2102
2232
|
|
|
2103
2233
|
@build.register
|
|
2104
2234
|
def _(self, base: Datasource):
|
|
2235
|
+
return self._build_datasource(base)
|
|
2236
|
+
|
|
2237
|
+
def _build_datasource(self, base: Datasource):
|
|
2105
2238
|
local_cache: dict[str, BuildConcept] = {}
|
|
2106
2239
|
factory = Factory(
|
|
2107
|
-
grain=base.grain,
|
|
2240
|
+
grain=base.grain,
|
|
2241
|
+
environment=self.environment,
|
|
2242
|
+
local_concepts=local_cache,
|
|
2243
|
+
pseudonym_map=self.pseudonym_map,
|
|
2108
2244
|
)
|
|
2109
|
-
return BuildDatasource
|
|
2245
|
+
return BuildDatasource(
|
|
2110
2246
|
name=base.name,
|
|
2111
|
-
columns=[factory.
|
|
2247
|
+
columns=[factory._build_column_assignment(c) for c in base.columns],
|
|
2112
2248
|
address=base.address,
|
|
2113
|
-
grain=factory.
|
|
2249
|
+
grain=factory._build_grain(base.grain),
|
|
2114
2250
|
namespace=base.namespace,
|
|
2115
2251
|
metadata=base.metadata,
|
|
2116
2252
|
where=(factory.build(base.where) if base.where else None),
|