pytrilogy 0.0.2.13__py3-none-any.whl → 0.0.2.15__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.2.13.dist-info → pytrilogy-0.0.2.15.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.2.13.dist-info → pytrilogy-0.0.2.15.dist-info}/RECORD +28 -28
- trilogy/__init__.py +1 -1
- trilogy/constants.py +12 -2
- trilogy/core/models.py +120 -11
- trilogy/core/optimizations/predicate_pushdown.py +1 -1
- trilogy/core/processing/node_generators/common.py +0 -13
- trilogy/core/processing/node_generators/filter_node.py +0 -14
- trilogy/core/processing/node_generators/group_node.py +19 -1
- trilogy/core/processing/node_generators/group_to_node.py +0 -12
- trilogy/core/processing/node_generators/multiselect_node.py +1 -10
- trilogy/core/processing/node_generators/rowset_node.py +17 -18
- trilogy/core/processing/node_generators/select_node.py +26 -0
- trilogy/core/processing/node_generators/window_node.py +1 -1
- trilogy/core/processing/nodes/base_node.py +28 -1
- trilogy/core/processing/nodes/group_node.py +31 -18
- trilogy/core/processing/nodes/merge_node.py +13 -4
- trilogy/core/processing/nodes/select_node_v2.py +4 -0
- trilogy/core/processing/utility.py +91 -3
- trilogy/core/query_processor.py +1 -2
- trilogy/dialect/common.py +10 -8
- trilogy/parsing/common.py +18 -2
- trilogy/parsing/parse_engine.py +13 -9
- trilogy/parsing/trilogy.lark +2 -2
- {pytrilogy-0.0.2.13.dist-info → pytrilogy-0.0.2.15.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.2.13.dist-info → pytrilogy-0.0.2.15.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.2.13.dist-info → pytrilogy-0.0.2.15.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.2.13.dist-info → pytrilogy-0.0.2.15.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
trilogy/__init__.py,sha256=blYi5mQGhWKBA8TAqC2T8y01GnT4mPEI1MaT2P3gwAI,291
|
|
2
2
|
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
trilogy/constants.py,sha256=
|
|
3
|
+
trilogy/constants.py,sha256=Ijos7_TEajKhZ7OJ_TreEYFddW1V33AVymDDrxP-ZHk,1234
|
|
4
4
|
trilogy/engine.py,sha256=R5ubIxYyrxRExz07aZCUfrTsoXCHQ8DKFTDsobXdWdA,1102
|
|
5
5
|
trilogy/executor.py,sha256=PZr7IF8wS1Oi2WJGE-B3lp70Y8ue2uuauODw02chjdQ,11175
|
|
6
6
|
trilogy/parser.py,sha256=UtuqSiGiCjpMAYgo1bvNq-b7NSzCA5hzbUW31RXaMII,281
|
|
@@ -16,42 +16,42 @@ trilogy/core/exceptions.py,sha256=NvV_4qLOgKXbpotgRf7c8BANDEvHxlqRPaA53IThQ2o,56
|
|
|
16
16
|
trilogy/core/functions.py,sha256=ARJAyBjeS415-54k3G_bx807rkPZonEulMaLRxSP7vU,10371
|
|
17
17
|
trilogy/core/graph_models.py,sha256=oJUMSpmYhqXlavckHLpR07GJxuQ8dZ1VbB1fB0KaS8c,2036
|
|
18
18
|
trilogy/core/internal.py,sha256=jNGFHKENnbMiMCtAgsnLZYVSENDK4b5ALecXFZpTDzQ,1075
|
|
19
|
-
trilogy/core/models.py,sha256=
|
|
19
|
+
trilogy/core/models.py,sha256=bWCklm8A9I0vx8fXWoN0jKJjLkXuyMUUQetT_zbhyRc,149031
|
|
20
20
|
trilogy/core/optimization.py,sha256=7E-Ol51u6ZAxF56F_bzLxgRO-Hu6Yl1ZbPopZJB2tqk,7533
|
|
21
|
-
trilogy/core/query_processor.py,sha256=
|
|
21
|
+
trilogy/core/query_processor.py,sha256=qMVkaK1Lvr9jEftJwAidMdkb_tRx12G07qynEyl91C8,18801
|
|
22
22
|
trilogy/core/optimizations/__init__.py,sha256=bWQecbeiwiDx9LJnLsa7dkWxdbl2wcnkcTN69JyP8iI,356
|
|
23
23
|
trilogy/core/optimizations/base_optimization.py,sha256=tWWT-xnTbnEU-mNi_isMNbywm8B9WTRsNFwGpeh3rqE,468
|
|
24
24
|
trilogy/core/optimizations/inline_constant.py,sha256=kHNyc2UoaPVdYfVAPAFwnWuk4sJ_IF5faRtVcDOrBtw,1110
|
|
25
25
|
trilogy/core/optimizations/inline_datasource.py,sha256=AATzQ6YrtW_1-aQFjQyTYqEYKBoMFhek7ADfBr4uUdQ,3634
|
|
26
|
-
trilogy/core/optimizations/predicate_pushdown.py,sha256=
|
|
26
|
+
trilogy/core/optimizations/predicate_pushdown.py,sha256=1l9WnFOSv79e341typG3tTdk0XGl1J_ToQih3LYoGIY,8435
|
|
27
27
|
trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
trilogy/core/processing/concept_strategies_v3.py,sha256=ae6FmwiKNiEbOU2GhnzggFMh82MhqxBj9bgr0ituT2w,25633
|
|
29
29
|
trilogy/core/processing/graph_utils.py,sha256=aq-kqk4Iado2HywDxWEejWc-7PGO6Oa-ZQLAM6XWPHw,1199
|
|
30
|
-
trilogy/core/processing/utility.py,sha256=
|
|
30
|
+
trilogy/core/processing/utility.py,sha256=jFLZmzxHq94q29FInr8XjS5YiqJOPPBYqh8Tlgs432Y,17722
|
|
31
31
|
trilogy/core/processing/node_generators/__init__.py,sha256=-mzYkRsaRNa_dfTckYkKVFSR8h8a3ihEiPJDU_tAmDo,672
|
|
32
32
|
trilogy/core/processing/node_generators/basic_node.py,sha256=IHj5jEloUe5yojGRLAzt35FcfHqGviWQdS8ETyvr39Q,3292
|
|
33
|
-
trilogy/core/processing/node_generators/common.py,sha256=
|
|
34
|
-
trilogy/core/processing/node_generators/filter_node.py,sha256=
|
|
35
|
-
trilogy/core/processing/node_generators/group_node.py,sha256=
|
|
36
|
-
trilogy/core/processing/node_generators/group_to_node.py,sha256=
|
|
37
|
-
trilogy/core/processing/node_generators/multiselect_node.py,sha256=
|
|
33
|
+
trilogy/core/processing/node_generators/common.py,sha256=3_Ivrq_wersDZ5pnvyHvsAUc07mRggxRGTiDq47O0Rk,8840
|
|
34
|
+
trilogy/core/processing/node_generators/filter_node.py,sha256=gCiv76Cu4idkZRyGkZG44BO50mTqrxYdUSDcZdpd0i4,7724
|
|
35
|
+
trilogy/core/processing/node_generators/group_node.py,sha256=G7SrU2X5kjgzeqzzpnPscQBTDcFMc4m7TR6n8VHLC_A,3762
|
|
36
|
+
trilogy/core/processing/node_generators/group_to_node.py,sha256=yX0uw6YMxhyWVRMZoMFzEkJe3tB5ByFqrTnuRWVcRh4,2446
|
|
37
|
+
trilogy/core/processing/node_generators/multiselect_node.py,sha256=OUjndYjA8xR6yKr-J7R-JxDeYfO6DxmMNNcJiFJzk7g,6138
|
|
38
38
|
trilogy/core/processing/node_generators/node_merge_node.py,sha256=D_jsnfoLMrQc08_JvT0wEDvjyzJAxBpdcZFyDN-feV0,13192
|
|
39
|
-
trilogy/core/processing/node_generators/rowset_node.py,sha256=
|
|
40
|
-
trilogy/core/processing/node_generators/select_node.py,sha256=
|
|
39
|
+
trilogy/core/processing/node_generators/rowset_node.py,sha256=tc8jt9bMq_HIdLM24sx_ivc7tTlpucQDEvsC2nkOtrY,4454
|
|
40
|
+
trilogy/core/processing/node_generators/select_node.py,sha256=XSMA4kvFdoXlfCpbciXXkbexXkemwUorcAU6P3EwuZY,19843
|
|
41
41
|
trilogy/core/processing/node_generators/unnest_node.py,sha256=aZeixbOzMtXi7BPahKr9bOkIhTciyD9Klsj0kZ56F6s,2189
|
|
42
|
-
trilogy/core/processing/node_generators/window_node.py,sha256=
|
|
42
|
+
trilogy/core/processing/node_generators/window_node.py,sha256=LSlXe41elFGVRlxRX3MEFimhduGn3o5WE0kLx2JtA4M,3322
|
|
43
43
|
trilogy/core/processing/nodes/__init__.py,sha256=jyduHk96j5fpju72sc8swOiBjR3Md866kt8JZGkp3ZU,4866
|
|
44
|
-
trilogy/core/processing/nodes/base_node.py,sha256=
|
|
44
|
+
trilogy/core/processing/nodes/base_node.py,sha256=11Evv2LErwlzCU9ebLWlzldz7VbVMgYiR_sUkVYylKQ,13916
|
|
45
45
|
trilogy/core/processing/nodes/filter_node.py,sha256=DBOSGFfkiILrZa1BlLv2uxUSkgWtSIKiZplqyKXPjg8,2132
|
|
46
|
-
trilogy/core/processing/nodes/group_node.py,sha256=
|
|
47
|
-
trilogy/core/processing/nodes/merge_node.py,sha256=
|
|
48
|
-
trilogy/core/processing/nodes/select_node_v2.py,sha256=
|
|
46
|
+
trilogy/core/processing/nodes/group_node.py,sha256=3zyEs11hv9CoGpO62cUKfClcS58clTUB0IMIkmOV998,6897
|
|
47
|
+
trilogy/core/processing/nodes/merge_node.py,sha256=bn7CwZwbYFx-OjNLb9oQuYL_abwAAd_KSSYJGFSEiP8,15022
|
|
48
|
+
trilogy/core/processing/nodes/select_node_v2.py,sha256=yoU2PWu-BkiUDECd7V7CKAPjznB_LObpl52HU9Sk5Yc,7433
|
|
49
49
|
trilogy/core/processing/nodes/unnest_node.py,sha256=mAmFluzm2yeeiQ6NfIB7BU_8atRGh-UJfPf9ROwbhr8,2152
|
|
50
50
|
trilogy/core/processing/nodes/window_node.py,sha256=X7qxLUKd3tekjUUsmH_4vz5b-U89gMnGd04VBxuu2Ns,1280
|
|
51
51
|
trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
52
|
trilogy/dialect/base.py,sha256=kQek_ufZC9HDVOKlWasvIx6xyew8wv3JNIU6r53_IR4,32842
|
|
53
53
|
trilogy/dialect/bigquery.py,sha256=15KJ-cOpBlk9O7FPviPgmg8xIydJeKx7WfmL3SSsPE8,2953
|
|
54
|
-
trilogy/dialect/common.py,sha256=
|
|
54
|
+
trilogy/dialect/common.py,sha256=QCsqo5morOOL6kwaCYh1RBmaInaoPI6lKtzdgroWvuM,3440
|
|
55
55
|
trilogy/dialect/config.py,sha256=tLVEMctaTDhUgARKXUNfHUcIolGaALkQ0RavUvXAY4w,2994
|
|
56
56
|
trilogy/dialect/duckdb.py,sha256=u_gpL35kouWxoBLas1h0ABYY2QzlVtEh22hm5h0lCOM,3182
|
|
57
57
|
trilogy/dialect/enums.py,sha256=4NdpsydBpDn6jnh0JzFz5VvQEtnShErWtWHVyT6TNpw,3948
|
|
@@ -65,18 +65,18 @@ trilogy/hooks/graph_hook.py,sha256=onHvMQPwj_KOS3HOTpRFiy7QLLKAiycq2MzJ_Q0Oh5Y,2
|
|
|
65
65
|
trilogy/hooks/query_debugger.py,sha256=NDChfkPmmW-KINa4TaQmDe_adGiwsKFdGLDSYpbodeU,4282
|
|
66
66
|
trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
67
|
trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
68
|
-
trilogy/parsing/common.py,sha256
|
|
68
|
+
trilogy/parsing/common.py,sha256=-4LM71ocidA8DI2RngqFEOmhzBrIt8VdBTO4x2BpD8E,9502
|
|
69
69
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
70
70
|
trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
|
|
71
71
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
72
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
72
|
+
trilogy/parsing/parse_engine.py,sha256=_DNpdZOQoZZio_mW8nxcCKoJHny7hnQQtOGG1msoCFU,63265
|
|
73
73
|
trilogy/parsing/render.py,sha256=Gy_6wVYPwYLf35Iota08sbqveuWILtUhI8MYStcvtJM,12174
|
|
74
|
-
trilogy/parsing/trilogy.lark,sha256
|
|
74
|
+
trilogy/parsing/trilogy.lark,sha256=-9y4oVAIlKi-6pE58G4RwGGTBeG7P3T_V4gV8mILd8w,11549
|
|
75
75
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
76
76
|
trilogy/scripts/trilogy.py,sha256=PHxvv6f2ODv0esyyhWxlARgra8dVhqQhYl0lTrSyVNo,3729
|
|
77
|
-
pytrilogy-0.0.2.
|
|
78
|
-
pytrilogy-0.0.2.
|
|
79
|
-
pytrilogy-0.0.2.
|
|
80
|
-
pytrilogy-0.0.2.
|
|
81
|
-
pytrilogy-0.0.2.
|
|
82
|
-
pytrilogy-0.0.2.
|
|
77
|
+
pytrilogy-0.0.2.15.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
78
|
+
pytrilogy-0.0.2.15.dist-info/METADATA,sha256=u9wzNtWDmhTjKdAmncckSd1lZv1j_Rkefq6kxifmvCI,7907
|
|
79
|
+
pytrilogy-0.0.2.15.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
80
|
+
pytrilogy-0.0.2.15.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
|
|
81
|
+
pytrilogy-0.0.2.15.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
82
|
+
pytrilogy-0.0.2.15.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/constants.py
CHANGED
|
@@ -30,7 +30,13 @@ class Optimizations:
|
|
|
30
30
|
|
|
31
31
|
@dataclass
|
|
32
32
|
class Comments:
|
|
33
|
-
|
|
33
|
+
"""Control what is placed in CTE comments"""
|
|
34
|
+
|
|
35
|
+
show: bool = False
|
|
36
|
+
basic: bool = True
|
|
37
|
+
joins: bool = True
|
|
38
|
+
nullable: bool = False
|
|
39
|
+
partial: bool = False
|
|
34
40
|
|
|
35
41
|
|
|
36
42
|
# TODO: support loading from environments
|
|
@@ -39,9 +45,13 @@ class Config:
|
|
|
39
45
|
strict_mode: bool = True
|
|
40
46
|
human_identifiers: bool = True
|
|
41
47
|
validate_missing: bool = True
|
|
42
|
-
|
|
48
|
+
comments: Comments = field(default_factory=Comments)
|
|
43
49
|
optimizations: Optimizations = field(default_factory=Optimizations)
|
|
44
50
|
|
|
51
|
+
@property
|
|
52
|
+
def show_comments(self) -> bool:
|
|
53
|
+
return self.comments.show
|
|
54
|
+
|
|
45
55
|
def set_random_seed(self, seed: int):
|
|
46
56
|
random.seed(seed)
|
|
47
57
|
|
trilogy/core/models.py
CHANGED
|
@@ -79,6 +79,18 @@ VT = TypeVar("VT")
|
|
|
79
79
|
LT = TypeVar("LT")
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
def is_compatible_datatype(left, right):
|
|
83
|
+
if left == right:
|
|
84
|
+
return True
|
|
85
|
+
if {left, right} == {DataType.NUMERIC, DataType.FLOAT}:
|
|
86
|
+
return True
|
|
87
|
+
if {left, right} == {DataType.NUMERIC, DataType.INTEGER}:
|
|
88
|
+
return True
|
|
89
|
+
if {left, right} == {DataType.FLOAT, DataType.INTEGER}:
|
|
90
|
+
return True
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
|
|
82
94
|
def get_version():
|
|
83
95
|
from trilogy import __version__
|
|
84
96
|
|
|
@@ -220,6 +232,7 @@ class DataType(Enum):
|
|
|
220
232
|
ARRAY = "array"
|
|
221
233
|
DATE_PART = "date_part"
|
|
222
234
|
STRUCT = "struct"
|
|
235
|
+
NULL = "null"
|
|
223
236
|
|
|
224
237
|
# GRANULAR
|
|
225
238
|
UNIX_SECONDS = "unix_seconds"
|
|
@@ -960,6 +973,10 @@ class ColumnAssignment(BaseModel):
|
|
|
960
973
|
def is_complete(self) -> bool:
|
|
961
974
|
return Modifier.PARTIAL not in self.modifiers
|
|
962
975
|
|
|
976
|
+
@property
|
|
977
|
+
def is_nullable(self) -> bool:
|
|
978
|
+
return Modifier.NULLABLE in self.modifiers
|
|
979
|
+
|
|
963
980
|
def with_namespace(self, namespace: str) -> "ColumnAssignment":
|
|
964
981
|
return ColumnAssignment(
|
|
965
982
|
alias=(
|
|
@@ -1065,9 +1082,12 @@ class Function(Mergeable, Namespaced, SelectContext, BaseModel):
|
|
|
1065
1082
|
]
|
|
1066
1083
|
]
|
|
1067
1084
|
|
|
1068
|
-
def
|
|
1085
|
+
def __repr__(self):
|
|
1069
1086
|
return f'{self.operator.value}({",".join([str(a) for a in self.arguments])})'
|
|
1070
1087
|
|
|
1088
|
+
def __str__(self):
|
|
1089
|
+
return self.__repr__()
|
|
1090
|
+
|
|
1071
1091
|
@property
|
|
1072
1092
|
def datatype(self):
|
|
1073
1093
|
return self.output_datatype
|
|
@@ -2072,6 +2092,10 @@ class Datasource(Namespaced, BaseModel):
|
|
|
2072
2092
|
def full_concepts(self) -> List[Concept]:
|
|
2073
2093
|
return [c.concept for c in self.columns if Modifier.PARTIAL not in c.modifiers]
|
|
2074
2094
|
|
|
2095
|
+
@property
|
|
2096
|
+
def nullable_concepts(self) -> List[Concept]:
|
|
2097
|
+
return [c.concept for c in self.columns if Modifier.NULLABLE in c.modifiers]
|
|
2098
|
+
|
|
2075
2099
|
@property
|
|
2076
2100
|
def output_concepts(self) -> List[Concept]:
|
|
2077
2101
|
return self.concepts
|
|
@@ -2135,13 +2159,27 @@ class InstantiatedUnnestJoin(BaseModel):
|
|
|
2135
2159
|
alias: str = "unnest"
|
|
2136
2160
|
|
|
2137
2161
|
|
|
2162
|
+
class ConceptPair(BaseModel):
|
|
2163
|
+
left: Concept
|
|
2164
|
+
right: Concept
|
|
2165
|
+
modifiers: List[Modifier] = Field(default_factory=list)
|
|
2166
|
+
|
|
2167
|
+
@property
|
|
2168
|
+
def is_partial(self):
|
|
2169
|
+
return Modifier.PARTIAL in self.modifiers
|
|
2170
|
+
|
|
2171
|
+
@property
|
|
2172
|
+
def is_nullable(self):
|
|
2173
|
+
return Modifier.NULLABLE in self.modifiers
|
|
2174
|
+
|
|
2175
|
+
|
|
2138
2176
|
class BaseJoin(BaseModel):
|
|
2139
2177
|
left_datasource: Union[Datasource, "QueryDatasource"]
|
|
2140
2178
|
right_datasource: Union[Datasource, "QueryDatasource"]
|
|
2141
2179
|
concepts: List[Concept]
|
|
2142
2180
|
join_type: JoinType
|
|
2143
2181
|
filter_to_mutual: bool = False
|
|
2144
|
-
concept_pairs: list[
|
|
2182
|
+
concept_pairs: list[ConceptPair] | None = None
|
|
2145
2183
|
|
|
2146
2184
|
def __init__(self, **data: Any):
|
|
2147
2185
|
super().__init__(**data)
|
|
@@ -2219,7 +2257,7 @@ class BaseJoin(BaseModel):
|
|
|
2219
2257
|
return (
|
|
2220
2258
|
f"{self.join_type.value} JOIN {self.left_datasource.identifier} and"
|
|
2221
2259
|
f" {self.right_datasource.identifier} on"
|
|
2222
|
-
f" {','.join([str(k
|
|
2260
|
+
f" {','.join([str(k.left)+'='+str(k.right) for k in self.concept_pairs])}"
|
|
2223
2261
|
)
|
|
2224
2262
|
return (
|
|
2225
2263
|
f"{self.join_type.value} JOIN {self.left_datasource.identifier} and"
|
|
@@ -2243,8 +2281,9 @@ class QueryDatasource(BaseModel):
|
|
|
2243
2281
|
filter_concepts: List[Concept] = Field(default_factory=list)
|
|
2244
2282
|
source_type: SourceType = SourceType.SELECT
|
|
2245
2283
|
partial_concepts: List[Concept] = Field(default_factory=list)
|
|
2246
|
-
join_derived_concepts: List[Concept] = Field(default_factory=list)
|
|
2247
2284
|
hidden_concepts: List[Concept] = Field(default_factory=list)
|
|
2285
|
+
nullable_concepts: List[Concept] = Field(default_factory=list)
|
|
2286
|
+
join_derived_concepts: List[Concept] = Field(default_factory=list)
|
|
2248
2287
|
force_group: bool | None = None
|
|
2249
2288
|
existence_source_map: Dict[str, Set[Union[Datasource, "QueryDatasource"]]] = Field(
|
|
2250
2289
|
default_factory=dict
|
|
@@ -2500,6 +2539,7 @@ class CTE(BaseModel):
|
|
|
2500
2539
|
joins: List[Union["Join", "InstantiatedUnnestJoin"]] = Field(default_factory=list)
|
|
2501
2540
|
condition: Optional[Union["Conditional", "Comparison", "Parenthetical"]] = None
|
|
2502
2541
|
partial_concepts: List[Concept] = Field(default_factory=list)
|
|
2542
|
+
nullable_concepts: List[Concept] = Field(default_factory=list)
|
|
2503
2543
|
join_derived_concepts: List[Concept] = Field(default_factory=list)
|
|
2504
2544
|
hidden_concepts: List[Concept] = Field(default_factory=list)
|
|
2505
2545
|
order_by: Optional[OrderBy] = None
|
|
@@ -2583,6 +2623,10 @@ class CTE(BaseModel):
|
|
|
2583
2623
|
base += f"\n-- Output: {', '.join([str(x) for x in self.output_columns])}."
|
|
2584
2624
|
if self.hidden_concepts:
|
|
2585
2625
|
base += f"\n-- Hidden: {', '.join([str(x) for x in self.hidden_concepts])}."
|
|
2626
|
+
if self.nullable_concepts:
|
|
2627
|
+
base += (
|
|
2628
|
+
f"\n-- Nullable: {', '.join([str(x) for x in self.nullable_concepts])}."
|
|
2629
|
+
)
|
|
2586
2630
|
|
|
2587
2631
|
return base
|
|
2588
2632
|
|
|
@@ -2677,6 +2721,9 @@ class CTE(BaseModel):
|
|
|
2677
2721
|
self.source.output_concepts = unique(
|
|
2678
2722
|
self.source.output_concepts + other.source.output_concepts, "address"
|
|
2679
2723
|
)
|
|
2724
|
+
self.nullable_concepts = unique(
|
|
2725
|
+
self.nullable_concepts + other.nullable_concepts, "address"
|
|
2726
|
+
)
|
|
2680
2727
|
self.hidden_concepts = mutually_hidden
|
|
2681
2728
|
self.existence_source_map = {
|
|
2682
2729
|
**self.existence_source_map,
|
|
@@ -2842,7 +2889,7 @@ class Join(BaseModel):
|
|
|
2842
2889
|
right_cte: CTE | Datasource
|
|
2843
2890
|
jointype: JoinType
|
|
2844
2891
|
joinkeys: List[JoinKey]
|
|
2845
|
-
joinkey_pairs: List[
|
|
2892
|
+
joinkey_pairs: List[ConceptPair] | None = None
|
|
2846
2893
|
|
|
2847
2894
|
@property
|
|
2848
2895
|
def left_name(self) -> str:
|
|
@@ -2877,7 +2924,7 @@ class Join(BaseModel):
|
|
|
2877
2924
|
return (
|
|
2878
2925
|
f"{self.jointype.value} JOIN {self.left_name} and"
|
|
2879
2926
|
f" {self.right_name} on"
|
|
2880
|
-
f" {','.join([str(k
|
|
2927
|
+
f" {','.join([str(k.left)+'='+str(k.right)+str(k.modifiers) for k in self.joinkey_pairs])}"
|
|
2881
2928
|
)
|
|
2882
2929
|
return (
|
|
2883
2930
|
f"{self.jointype.value} JOIN {self.left_name} and"
|
|
@@ -3450,11 +3497,41 @@ class Comparison(
|
|
|
3450
3497
|
]
|
|
3451
3498
|
operator: ComparisonOperator
|
|
3452
3499
|
|
|
3453
|
-
def
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3500
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
3501
|
+
super().__init__(*args, **kwargs)
|
|
3502
|
+
if self.operator in (ComparisonOperator.IS, ComparisonOperator.IS_NOT):
|
|
3503
|
+
if self.right != MagicConstants.NULL and DataType.BOOL != arg_to_datatype(
|
|
3504
|
+
self.right
|
|
3505
|
+
):
|
|
3506
|
+
raise SyntaxError(
|
|
3507
|
+
f"Cannot use {self.operator.value} with non-null or boolean value {self.right}"
|
|
3508
|
+
)
|
|
3509
|
+
elif self.operator in (ComparisonOperator.IN, ComparisonOperator.NOT_IN):
|
|
3510
|
+
right = arg_to_datatype(self.right)
|
|
3511
|
+
if not isinstance(self.right, Concept) and not isinstance(right, ListType):
|
|
3512
|
+
raise SyntaxError(
|
|
3513
|
+
f"Cannot use {self.operator.value} with non-list type {right} in {str(self)}"
|
|
3514
|
+
)
|
|
3515
|
+
|
|
3516
|
+
elif isinstance(right, ListType) and not is_compatible_datatype(
|
|
3517
|
+
arg_to_datatype(self.left), right.value_data_type
|
|
3518
|
+
):
|
|
3519
|
+
raise SyntaxError(
|
|
3520
|
+
f"Cannot compare {arg_to_datatype(self.left)} and {right} with operator {self.operator} in {str(self)}"
|
|
3521
|
+
)
|
|
3522
|
+
elif isinstance(self.right, Concept) and not is_compatible_datatype(
|
|
3523
|
+
arg_to_datatype(self.left), arg_to_datatype(self.right)
|
|
3524
|
+
):
|
|
3525
|
+
raise SyntaxError(
|
|
3526
|
+
f"Cannot compare {arg_to_datatype(self.left)} and {arg_to_datatype(self.right)} with operator {self.operator} in {str(self)}"
|
|
3527
|
+
)
|
|
3528
|
+
else:
|
|
3529
|
+
if not is_compatible_datatype(
|
|
3530
|
+
arg_to_datatype(self.left), arg_to_datatype(self.right)
|
|
3531
|
+
):
|
|
3532
|
+
raise SyntaxError(
|
|
3533
|
+
f"Cannot compare {arg_to_datatype(self.left)} and {arg_to_datatype(self.right)} of different types with operator {self.operator} in {str(self)}"
|
|
3534
|
+
)
|
|
3458
3535
|
|
|
3459
3536
|
def __add__(self, other):
|
|
3460
3537
|
if other is None:
|
|
@@ -3655,6 +3732,12 @@ class CaseWhen(Namespaced, SelectContext, BaseModel):
|
|
|
3655
3732
|
comparison: Conditional | SubselectComparison | Comparison
|
|
3656
3733
|
expr: "Expr"
|
|
3657
3734
|
|
|
3735
|
+
def __str__(self):
|
|
3736
|
+
return self.__repr__()
|
|
3737
|
+
|
|
3738
|
+
def __repr__(self):
|
|
3739
|
+
return f"WHEN {str(self.comparison)} THEN {str(self.expr)}"
|
|
3740
|
+
|
|
3658
3741
|
@property
|
|
3659
3742
|
def concept_arguments(self):
|
|
3660
3743
|
return get_concept_arguments(self.comparison) + get_concept_arguments(self.expr)
|
|
@@ -4347,6 +4430,7 @@ class ShowStatement(BaseModel):
|
|
|
4347
4430
|
|
|
4348
4431
|
Expr = (
|
|
4349
4432
|
bool
|
|
4433
|
+
| MagicConstants
|
|
4350
4434
|
| int
|
|
4351
4435
|
| str
|
|
4352
4436
|
| float
|
|
@@ -4401,9 +4485,34 @@ def dict_to_map_wrapper(arg):
|
|
|
4401
4485
|
return MapWrapper(arg, key_type=key_types[0], value_type=value_types[0])
|
|
4402
4486
|
|
|
4403
4487
|
|
|
4488
|
+
def merge_datatypes(
|
|
4489
|
+
inputs: list[DataType | ListType | StructType | MapType | NumericType],
|
|
4490
|
+
) -> DataType | ListType | StructType | MapType | NumericType:
|
|
4491
|
+
"""This is a temporary hack for doing between
|
|
4492
|
+
allowable datatype transformation matrix"""
|
|
4493
|
+
if len(inputs) == 1:
|
|
4494
|
+
return inputs[0]
|
|
4495
|
+
if set(inputs) == {DataType.INTEGER, DataType.FLOAT}:
|
|
4496
|
+
return DataType.FLOAT
|
|
4497
|
+
if set(inputs) == {DataType.INTEGER, DataType.NUMERIC}:
|
|
4498
|
+
return DataType.NUMERIC
|
|
4499
|
+
if any(isinstance(x, NumericType) for x in inputs) and all(
|
|
4500
|
+
isinstance(x, NumericType)
|
|
4501
|
+
or x in (DataType.INTEGER, DataType.FLOAT, DataType.NUMERIC)
|
|
4502
|
+
for x in inputs
|
|
4503
|
+
):
|
|
4504
|
+
candidate = next(x for x in inputs if isinstance(x, NumericType))
|
|
4505
|
+
return candidate
|
|
4506
|
+
return inputs[0]
|
|
4507
|
+
|
|
4508
|
+
|
|
4404
4509
|
def arg_to_datatype(arg) -> DataType | ListType | StructType | MapType | NumericType:
|
|
4405
4510
|
if isinstance(arg, Function):
|
|
4406
4511
|
return arg.output_datatype
|
|
4512
|
+
elif isinstance(arg, MagicConstants):
|
|
4513
|
+
if arg == MagicConstants.NULL:
|
|
4514
|
+
return DataType.NULL
|
|
4515
|
+
raise ValueError(f"Cannot parse arg datatype for arg of type {arg}")
|
|
4407
4516
|
elif isinstance(arg, Concept):
|
|
4408
4517
|
return arg.datatype
|
|
4409
4518
|
elif isinstance(arg, bool):
|
|
@@ -135,7 +135,7 @@ class PredicatePushdown(OptimizationRule):
|
|
|
135
135
|
f"Skipping {candidate} as not a basic [no aggregate, etc] condition"
|
|
136
136
|
)
|
|
137
137
|
continue
|
|
138
|
-
self.
|
|
138
|
+
self.debug(
|
|
139
139
|
f"Checking candidate {candidate}, {type(candidate)}, scalar: {is_scalar_condition(candidate)}"
|
|
140
140
|
)
|
|
141
141
|
for parent_cte in cte.parent_ctes:
|
|
@@ -15,12 +15,10 @@ from trilogy.utility import unique
|
|
|
15
15
|
from trilogy.core.processing.nodes.base_node import StrategyNode
|
|
16
16
|
from trilogy.core.processing.nodes.merge_node import MergeNode
|
|
17
17
|
from trilogy.core.processing.nodes import History
|
|
18
|
-
from trilogy.core.enums import JoinType
|
|
19
18
|
from trilogy.core.processing.nodes import (
|
|
20
19
|
NodeJoin,
|
|
21
20
|
)
|
|
22
21
|
from collections import defaultdict
|
|
23
|
-
from trilogy.core.processing.utility import concept_to_relevant_joins
|
|
24
22
|
|
|
25
23
|
|
|
26
24
|
def resolve_function_parent_concepts(concept: Concept) -> List[Concept]:
|
|
@@ -218,17 +216,6 @@ def gen_enrichment_node(
|
|
|
218
216
|
g=g,
|
|
219
217
|
parents=[enrich_node, base_node],
|
|
220
218
|
force_group=False,
|
|
221
|
-
node_joins=[
|
|
222
|
-
NodeJoin(
|
|
223
|
-
left_node=enrich_node,
|
|
224
|
-
right_node=base_node,
|
|
225
|
-
concepts=concept_to_relevant_joins(
|
|
226
|
-
[x for x in join_keys if x in enrich_node.output_lcl]
|
|
227
|
-
),
|
|
228
|
-
filter_to_mutual=False,
|
|
229
|
-
join_type=JoinType.LEFT_OUTER,
|
|
230
|
-
)
|
|
231
|
-
],
|
|
232
219
|
)
|
|
233
220
|
|
|
234
221
|
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
from trilogy.core.enums import JoinType
|
|
5
4
|
from trilogy.core.models import Concept, Environment, FilterItem, Grain, WhereClause
|
|
6
5
|
from trilogy.core.processing.nodes import (
|
|
7
6
|
FilterNode,
|
|
8
7
|
MergeNode,
|
|
9
|
-
NodeJoin,
|
|
10
8
|
History,
|
|
11
9
|
StrategyNode,
|
|
12
10
|
SelectNode,
|
|
@@ -16,7 +14,6 @@ from trilogy.core.processing.node_generators.common import (
|
|
|
16
14
|
)
|
|
17
15
|
from trilogy.constants import logger
|
|
18
16
|
from trilogy.core.processing.utility import padding, unique
|
|
19
|
-
from trilogy.core.processing.node_generators.common import concept_to_relevant_joins
|
|
20
17
|
from trilogy.core.processing.utility import is_scalar_condition
|
|
21
18
|
|
|
22
19
|
LOGGER_PREFIX = "[GEN_FILTER_NODE]"
|
|
@@ -216,15 +213,4 @@ def gen_filter_node(
|
|
|
216
213
|
filter_node,
|
|
217
214
|
enrich_node,
|
|
218
215
|
],
|
|
219
|
-
node_joins=[
|
|
220
|
-
NodeJoin(
|
|
221
|
-
left_node=enrich_node,
|
|
222
|
-
right_node=filter_node,
|
|
223
|
-
concepts=concept_to_relevant_joins(
|
|
224
|
-
[immediate_parent] + parent_row_concepts
|
|
225
|
-
),
|
|
226
|
-
join_type=JoinType.LEFT_OUTER,
|
|
227
|
-
filter_to_mutual=True,
|
|
228
|
-
)
|
|
229
|
-
],
|
|
230
216
|
)
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
from trilogy.core.models import
|
|
1
|
+
from trilogy.core.models import (
|
|
2
|
+
Concept,
|
|
3
|
+
Environment,
|
|
4
|
+
LooseConceptList,
|
|
5
|
+
WhereClause,
|
|
6
|
+
Function,
|
|
7
|
+
AggregateWrapper,
|
|
8
|
+
)
|
|
2
9
|
from trilogy.utility import unique
|
|
3
10
|
from trilogy.core.processing.nodes import GroupNode, StrategyNode, History
|
|
4
11
|
from typing import List
|
|
@@ -42,6 +49,17 @@ def gen_group_node(
|
|
|
42
49
|
)
|
|
43
50
|
parent_concepts += grain_components
|
|
44
51
|
output_concepts += grain_components
|
|
52
|
+
for possible_agg in local_optional:
|
|
53
|
+
if possible_agg.grain and possible_agg.grain == concept.grain:
|
|
54
|
+
if not isinstance(possible_agg.lineage, (AggregateWrapper, Function)):
|
|
55
|
+
continue
|
|
56
|
+
agg_parents: List[Concept] = resolve_function_parent_concepts(
|
|
57
|
+
possible_agg
|
|
58
|
+
)
|
|
59
|
+
if set([x.address for x in agg_parents]).issubset(
|
|
60
|
+
set([x.address for x in parent_concepts])
|
|
61
|
+
):
|
|
62
|
+
output_concepts.append(possible_agg)
|
|
45
63
|
|
|
46
64
|
if parent_concepts:
|
|
47
65
|
logger.info(
|
|
@@ -3,15 +3,12 @@ from trilogy.core.processing.nodes import (
|
|
|
3
3
|
GroupNode,
|
|
4
4
|
StrategyNode,
|
|
5
5
|
MergeNode,
|
|
6
|
-
NodeJoin,
|
|
7
6
|
History,
|
|
8
7
|
)
|
|
9
8
|
from typing import List
|
|
10
|
-
from trilogy.core.enums import JoinType
|
|
11
9
|
|
|
12
10
|
from trilogy.constants import logger
|
|
13
11
|
from trilogy.core.processing.utility import padding
|
|
14
|
-
from trilogy.core.processing.node_generators.common import concept_to_relevant_joins
|
|
15
12
|
|
|
16
13
|
LOGGER_PREFIX = "[GEN_GROUP_TO_NODE]"
|
|
17
14
|
|
|
@@ -84,15 +81,6 @@ def gen_group_to_node(
|
|
|
84
81
|
# this node gets enrichment
|
|
85
82
|
enrich_node,
|
|
86
83
|
],
|
|
87
|
-
node_joins=[
|
|
88
|
-
NodeJoin(
|
|
89
|
-
left_node=group_node,
|
|
90
|
-
right_node=enrich_node,
|
|
91
|
-
concepts=concept_to_relevant_joins(parent_concepts),
|
|
92
|
-
filter_to_mutual=False,
|
|
93
|
-
join_type=JoinType.LEFT_OUTER,
|
|
94
|
-
)
|
|
95
|
-
],
|
|
96
84
|
whole_grain=True,
|
|
97
85
|
depth=depth,
|
|
98
86
|
)
|
|
@@ -10,7 +10,7 @@ from typing import List
|
|
|
10
10
|
from trilogy.core.enums import JoinType
|
|
11
11
|
from trilogy.constants import logger
|
|
12
12
|
from trilogy.core.processing.utility import padding
|
|
13
|
-
from trilogy.core.processing.
|
|
13
|
+
from trilogy.core.processing.utility import concept_to_relevant_joins
|
|
14
14
|
from collections import defaultdict
|
|
15
15
|
from itertools import combinations
|
|
16
16
|
from trilogy.core.enums import Purpose
|
|
@@ -176,14 +176,5 @@ def gen_multiselect_node(
|
|
|
176
176
|
# this node gets enrichment
|
|
177
177
|
enrich_node,
|
|
178
178
|
],
|
|
179
|
-
node_joins=[
|
|
180
|
-
NodeJoin(
|
|
181
|
-
left_node=enrich_node,
|
|
182
|
-
right_node=node,
|
|
183
|
-
concepts=possible_joins,
|
|
184
|
-
filter_to_mutual=False,
|
|
185
|
-
join_type=JoinType.LEFT_OUTER,
|
|
186
|
-
)
|
|
187
|
-
],
|
|
188
179
|
partial_concepts=node.partial_concepts,
|
|
189
180
|
)
|
|
@@ -6,14 +6,14 @@ from trilogy.core.models import (
|
|
|
6
6
|
RowsetItem,
|
|
7
7
|
MultiSelectStatement,
|
|
8
8
|
)
|
|
9
|
-
from trilogy.core.processing.nodes import MergeNode,
|
|
9
|
+
from trilogy.core.processing.nodes import MergeNode, History, StrategyNode
|
|
10
10
|
from trilogy.core.processing.nodes.base_node import concept_list_to_grain
|
|
11
11
|
from typing import List
|
|
12
12
|
|
|
13
|
-
from trilogy.core.enums import
|
|
13
|
+
from trilogy.core.enums import PurposeLineage
|
|
14
14
|
from trilogy.constants import logger
|
|
15
15
|
from trilogy.core.processing.utility import padding
|
|
16
|
-
from trilogy.core.processing.
|
|
16
|
+
from trilogy.core.processing.utility import concept_to_relevant_joins
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
LOGGER_PREFIX = "[GEN_ROWSET_NODE]"
|
|
@@ -71,12 +71,22 @@ def gen_rowset_node(
|
|
|
71
71
|
and x.derivation != PurposeLineage.ROWSET
|
|
72
72
|
]
|
|
73
73
|
node.hide_output_concepts(final_hidden)
|
|
74
|
-
# assume grain to be output of select
|
|
75
|
-
# but don't include anything aggregate at this point
|
|
76
|
-
assert node.resolution_cache
|
|
77
74
|
|
|
75
|
+
assert node.resolution_cache
|
|
76
|
+
# assume grain to be output of select
|
|
77
|
+
# but don't include anything hidden(the non-rowset concepts)
|
|
78
78
|
node.grain = concept_list_to_grain(
|
|
79
|
-
|
|
79
|
+
[
|
|
80
|
+
x
|
|
81
|
+
for x in node.output_concepts
|
|
82
|
+
if x.address
|
|
83
|
+
not in [
|
|
84
|
+
y.address
|
|
85
|
+
for y in node.hidden_concepts
|
|
86
|
+
if y.derivation != PurposeLineage.ROWSET
|
|
87
|
+
]
|
|
88
|
+
],
|
|
89
|
+
parent_sources=node.resolution_cache.datasources,
|
|
80
90
|
)
|
|
81
91
|
|
|
82
92
|
node.rebuild_cache()
|
|
@@ -113,19 +123,8 @@ def gen_rowset_node(
|
|
|
113
123
|
g=g,
|
|
114
124
|
depth=depth,
|
|
115
125
|
parents=[
|
|
116
|
-
# this node gets the window
|
|
117
126
|
node,
|
|
118
|
-
# this node gets enrichment
|
|
119
127
|
enrich_node,
|
|
120
128
|
],
|
|
121
|
-
node_joins=[
|
|
122
|
-
NodeJoin(
|
|
123
|
-
left_node=enrich_node,
|
|
124
|
-
right_node=node,
|
|
125
|
-
concepts=concept_to_relevant_joins(additional_relevant),
|
|
126
|
-
filter_to_mutual=False,
|
|
127
|
-
join_type=JoinType.LEFT_OUTER,
|
|
128
|
-
)
|
|
129
|
-
],
|
|
130
129
|
partial_concepts=node.partial_concepts,
|
|
131
130
|
)
|