pytrilogy 0.0.3.85__py3-none-any.whl → 0.0.3.88__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.85.dist-info → pytrilogy-0.0.3.88.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.3.85.dist-info → pytrilogy-0.0.3.88.dist-info}/RECORD +18 -18
- trilogy/__init__.py +1 -1
- trilogy/core/enums.py +9 -0
- trilogy/core/functions.py +31 -1
- trilogy/core/models/author.py +25 -12
- trilogy/core/models/build.py +5 -2
- trilogy/core/models/core.py +20 -3
- trilogy/core/models/execute.py +5 -0
- trilogy/dialect/base.py +33 -14
- trilogy/dialect/duckdb.py +1 -0
- trilogy/dialect/presto.py +2 -1
- trilogy/parsing/parse_engine.py +39 -7
- trilogy/parsing/trilogy.lark +14 -5
- {pytrilogy-0.0.3.85.dist-info → pytrilogy-0.0.3.88.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.85.dist-info → pytrilogy-0.0.3.88.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.85.dist-info → pytrilogy-0.0.3.88.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.85.dist-info → pytrilogy-0.0.3.88.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
pytrilogy-0.0.3.
|
|
2
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
pytrilogy-0.0.3.88.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
2
|
+
trilogy/__init__.py,sha256=cl4nRewdhba3xQodaj4EqSuH_HCoF56PNkWEO79F_VY,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
|
|
@@ -11,25 +11,25 @@ trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
|
|
|
11
11
|
trilogy/authoring/__init__.py,sha256=e74k-Jep4DLYPQU_2m0aVsYlw5HKTOucAKtdTbd6f2g,2595
|
|
12
12
|
trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
trilogy/core/constants.py,sha256=nizWYDCJQ1bigQMtkNIEMNTcN0NoEAXiIHLzpelxQ24,201
|
|
14
|
-
trilogy/core/enums.py,sha256=
|
|
14
|
+
trilogy/core/enums.py,sha256=RQRkpGHLtcBKAO6jZnmGVtSUnb00Q2rP56ltYGdfTok,8294
|
|
15
15
|
trilogy/core/env_processor.py,sha256=pFsxnluKIusGKx1z7tTnfsd_xZcPy9pZDungkjkyvI0,3170
|
|
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=piMv91RiW1OAgDS5W8HIiO6fGleiNXIFW0YnNZgYOWU,32943
|
|
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=
|
|
27
|
-
trilogy/core/models/build.py,sha256=
|
|
26
|
+
trilogy/core/models/author.py,sha256=5o0x9d3U_J2b8GPdJnMsMruC2P-xiI55lZKWUwRIeGU,78936
|
|
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=eSzrrscwTrlO_PIn9_UxKSPBgKgR9-xp90LY1e0lhN0,11538
|
|
30
30
|
trilogy/core/models/datasource.py,sha256=wogTevZ-9CyUW2a8gjzqMCieircxi-J5lkI7EOAZnck,9596
|
|
31
31
|
trilogy/core/models/environment.py,sha256=0IHSCFf5e5b4LPQN3vmjumtfM1iD1tN4WMoUr0UqxZI,27855
|
|
32
|
-
trilogy/core/models/execute.py,sha256=
|
|
32
|
+
trilogy/core/models/execute.py,sha256=sVWhjwWull-T6pUJizhrYVGCWHY3eZivVN6KNlhcHig,41839
|
|
33
33
|
trilogy/core/optimizations/__init__.py,sha256=YH2-mGXZnVDnBcWVi8vTbrdw7Qs5TivG4h38rH3js_I,290
|
|
34
34
|
trilogy/core/optimizations/base_optimization.py,sha256=gzDOKImoFn36k7XBD3ysEYDnbnb6vdVIztUfFQZsGnM,513
|
|
35
35
|
trilogy/core/optimizations/inline_datasource.py,sha256=2sWNRpoRInnTgo9wExVT_r9RfLAQHI57reEV5cGHUcg,4329
|
|
@@ -77,15 +77,15 @@ 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=
|
|
80
|
+
trilogy/dialect/base.py,sha256=bkCJsJvUQLlrKzHCTbWlLikas5o-JluCVI12RSSbYuE,48228
|
|
81
81
|
trilogy/dialect/bigquery.py,sha256=8xhEu0z_lKANjbvzvBbC7CeKrJf1iP8YyrHqNale-ug,4351
|
|
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
|
|
85
|
-
trilogy/dialect/duckdb.py,sha256=
|
|
85
|
+
trilogy/dialect/duckdb.py,sha256=JoUvQ19WvgxoaJkGLM7DPXOd1H0394k3vBiblksQzOI,5631
|
|
86
86
|
trilogy/dialect/enums.py,sha256=FRNYQ5-w-B6-X0yXKNU5g9GowsMlERFogTC5u2nxL_s,4740
|
|
87
87
|
trilogy/dialect/postgres.py,sha256=el2PKwfyvWGk5EZtLudqAH5ewLitY1sFHJiocBSyxyM,3393
|
|
88
|
-
trilogy/dialect/presto.py,sha256=
|
|
88
|
+
trilogy/dialect/presto.py,sha256=k1IaeilR3nzPC9Hp7jlAdzJ7TsuxB3LQTBQ28MYE7O8,3715
|
|
89
89
|
trilogy/dialect/snowflake.py,sha256=T6_mKfhpDazB1xQxqFLS2AJwzwzBcPYY6_qxRnAtFBs,3326
|
|
90
90
|
trilogy/dialect/sql_server.py,sha256=HnUEvb8Yjl6MnMWTITzpFPZgDajhGzfDPz5A8dDerak,3279
|
|
91
91
|
trilogy/hooks/__init__.py,sha256=T3SF3phuUDPLXKGRVE_Lf9mzuwoXWyaLolncR_1kY30,144
|
|
@@ -98,9 +98,9 @@ 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=snT7m7LWrOHcSZlqdJ5HXWmH1wMMPDaqrsf6AQsMx70,79800
|
|
102
102
|
trilogy/parsing/render.py,sha256=HSNntD82GiiwHT-TWPLXAaIMWLYVV5B5zQEsgwrHIBE,19605
|
|
103
|
-
trilogy/parsing/trilogy.lark,sha256=
|
|
103
|
+
trilogy/parsing/trilogy.lark,sha256=ySzMMLxyPjn74MjFHZxXPTW-jHW68KLPJpiszPvZaO0,15780
|
|
104
104
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
105
105
|
trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
|
|
106
106
|
trilogy/std/__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.88.dist-info/METADATA,sha256=MJ92RHQZuHjfV6eUuy9Z_F7U3BBt4ilDIzrfkS_kzSg,9589
|
|
115
|
+
pytrilogy-0.0.3.88.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
116
|
+
pytrilogy-0.0.3.88.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
|
|
117
|
+
pytrilogy-0.0.3.88.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
118
|
+
pytrilogy-0.0.3.88.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/enums.py
CHANGED
|
@@ -13,6 +13,11 @@ class UnnestMode(Enum):
|
|
|
13
13
|
SNOWFLAKE = "snowflake"
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
class GroupMode(Enum):
|
|
17
|
+
AUTO = "auto"
|
|
18
|
+
BY_INDEX = "by_index"
|
|
19
|
+
|
|
20
|
+
|
|
16
21
|
class ConceptSource(Enum):
|
|
17
22
|
MANUAL = "manual"
|
|
18
23
|
CTE = "cte"
|
|
@@ -156,6 +161,10 @@ class FunctionType(Enum):
|
|
|
156
161
|
ARRAY_TRANSFORM = "array_transform"
|
|
157
162
|
ARRAY_TO_STRING = "array_to_string"
|
|
158
163
|
|
|
164
|
+
# MAP
|
|
165
|
+
MAP_KEYS = "map_keys"
|
|
166
|
+
MAP_VALUES = "map_values"
|
|
167
|
+
|
|
159
168
|
# TEXT AND MAYBE MORE
|
|
160
169
|
SPLIT = "split"
|
|
161
170
|
LENGTH = "len"
|
trilogy/core/functions.py
CHANGED
|
@@ -70,7 +70,7 @@ def get_coalesce_output_type(args: list[Any]) -> CONCRETE_TYPES:
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
def get_transform_output_type(args: list[Any]) -> CONCRETE_TYPES:
|
|
73
|
-
return arg_to_datatype(args[2])
|
|
73
|
+
return ArrayType(type=arg_to_datatype(args[2]))
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
def get_index_output_type(
|
|
@@ -178,6 +178,20 @@ def get_date_trunc_output(
|
|
|
178
178
|
raise InvalidSyntaxException(f"Date truncation not supported for {target}")
|
|
179
179
|
|
|
180
180
|
|
|
181
|
+
def get_map_key_type(arg):
|
|
182
|
+
arg_datatype = arg_to_datatype(arg)
|
|
183
|
+
if isinstance(arg_datatype, MapType):
|
|
184
|
+
return ArrayType(type=arg_datatype.key_data_type)
|
|
185
|
+
return ArrayType(type=DataType.STRING)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def get_map_value_type(arg):
|
|
189
|
+
arg_datatype = arg_to_datatype(arg)
|
|
190
|
+
if isinstance(arg_datatype, MapType):
|
|
191
|
+
return ArrayType(type=arg_datatype.value_data_type)
|
|
192
|
+
return ArrayType(type=DataType.STRING)
|
|
193
|
+
|
|
194
|
+
|
|
181
195
|
FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
182
196
|
FunctionType.ALIAS: FunctionConfig(
|
|
183
197
|
arg_count=1,
|
|
@@ -254,6 +268,22 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
254
268
|
output_type_function=get_index_output_type,
|
|
255
269
|
arg_count=2,
|
|
256
270
|
),
|
|
271
|
+
FunctionType.MAP_KEYS: FunctionConfig(
|
|
272
|
+
valid_inputs={
|
|
273
|
+
DataType.MAP,
|
|
274
|
+
},
|
|
275
|
+
output_purpose=Purpose.PROPERTY,
|
|
276
|
+
output_type_function=lambda args: get_map_key_type(args[0]),
|
|
277
|
+
arg_count=1,
|
|
278
|
+
),
|
|
279
|
+
FunctionType.MAP_VALUES: FunctionConfig(
|
|
280
|
+
valid_inputs={
|
|
281
|
+
DataType.MAP,
|
|
282
|
+
},
|
|
283
|
+
output_purpose=Purpose.PROPERTY,
|
|
284
|
+
output_type_function=lambda args: get_map_value_type(args[0]),
|
|
285
|
+
arg_count=1,
|
|
286
|
+
),
|
|
257
287
|
FunctionType.ARRAY_DISTINCT: FunctionConfig(
|
|
258
288
|
valid_inputs={
|
|
259
289
|
DataType.ARRAY,
|
trilogy/core/models/author.py
CHANGED
|
@@ -164,6 +164,21 @@ class ConceptRef(Addressable, Namespaced, DataTyped, Mergeable, BaseModel):
|
|
|
164
164
|
def with_reference_replacement(self, source: str, target: Expr | ArgBinding):
|
|
165
165
|
if self.address == source:
|
|
166
166
|
return target
|
|
167
|
+
|
|
168
|
+
# a reference might be to an attribute of a struct that is bound late
|
|
169
|
+
# if the replacement is a parent in the access path; replace reference
|
|
170
|
+
# with an attribute access call
|
|
171
|
+
candidates = [f"local.{self.address}", self.address]
|
|
172
|
+
for candidate in candidates:
|
|
173
|
+
if not candidate.startswith(f"{source}."):
|
|
174
|
+
continue
|
|
175
|
+
return Function(
|
|
176
|
+
arguments=[target, self.address.rsplit(".", 1)[1]],
|
|
177
|
+
operator=FunctionType.ATTR_ACCESS,
|
|
178
|
+
arg_count=2,
|
|
179
|
+
output_datatype=arg_to_datatype(target),
|
|
180
|
+
output_purpose=Purpose.PROPERTY,
|
|
181
|
+
)
|
|
167
182
|
return self
|
|
168
183
|
|
|
169
184
|
|
|
@@ -637,33 +652,31 @@ class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
|
|
|
637
652
|
|
|
638
653
|
@model_validator(mode="after")
|
|
639
654
|
def validate_comparison(self):
|
|
655
|
+
left_type = arg_to_datatype(self.left)
|
|
656
|
+
right_type = arg_to_datatype(self.right)
|
|
640
657
|
if self.operator in (ComparisonOperator.IS, ComparisonOperator.IS_NOT):
|
|
641
|
-
if self.right != MagicConstants.NULL and DataType.BOOL !=
|
|
642
|
-
self.right
|
|
643
|
-
):
|
|
658
|
+
if self.right != MagicConstants.NULL and DataType.BOOL != right_type:
|
|
644
659
|
raise SyntaxError(
|
|
645
660
|
f"Cannot use {self.operator.value} with non-null or boolean value {self.right}"
|
|
646
661
|
)
|
|
647
662
|
elif self.operator in (ComparisonOperator.IN, ComparisonOperator.NOT_IN):
|
|
648
|
-
|
|
663
|
+
|
|
649
664
|
if isinstance(right_type, ArrayType) and not is_compatible_datatype(
|
|
650
|
-
|
|
665
|
+
left_type, right_type.value_data_type
|
|
651
666
|
):
|
|
652
667
|
raise SyntaxError(
|
|
653
|
-
f"Cannot compare {
|
|
668
|
+
f"Cannot compare {left_type} and {right_type} with operator {self.operator} in {str(self)}"
|
|
654
669
|
)
|
|
655
670
|
elif isinstance(self.right, Concept) and not is_compatible_datatype(
|
|
656
|
-
|
|
671
|
+
left_type, right_type
|
|
657
672
|
):
|
|
658
673
|
raise SyntaxError(
|
|
659
|
-
f"Cannot compare {
|
|
674
|
+
f"Cannot compare {left_type.name} and {right_type.name} with operator {self.operator} in {str(self)}"
|
|
660
675
|
)
|
|
661
676
|
else:
|
|
662
|
-
if not is_compatible_datatype(
|
|
663
|
-
arg_to_datatype(self.left), arg_to_datatype(self.right)
|
|
664
|
-
):
|
|
677
|
+
if not is_compatible_datatype(left_type, right_type):
|
|
665
678
|
raise SyntaxError(
|
|
666
|
-
f"Cannot compare {
|
|
679
|
+
f"Cannot compare {left_type.name} ({self.left}) and {right_type.name} ({self.right}) of different types with operator {self.operator.value} in {str(self)}"
|
|
667
680
|
)
|
|
668
681
|
|
|
669
682
|
return self
|
trilogy/core/models/build.py
CHANGED
|
@@ -1649,8 +1649,11 @@ class Factory:
|
|
|
1649
1649
|
full = self.local_concepts[base.address]
|
|
1650
1650
|
if isinstance(full, BuildConcept):
|
|
1651
1651
|
return full
|
|
1652
|
-
|
|
1653
|
-
|
|
1652
|
+
if base.address in self.environment.concepts:
|
|
1653
|
+
raw = self.environment.concepts[base.address]
|
|
1654
|
+
return self.build(raw)
|
|
1655
|
+
# this will error by design - TODO - more helpful message?
|
|
1656
|
+
return self.build(self.environment.concepts[base.address])
|
|
1654
1657
|
|
|
1655
1658
|
@build.register
|
|
1656
1659
|
def _(self, base: CaseWhen) -> BuildCaseWhen:
|
trilogy/core/models/core.py
CHANGED
|
@@ -27,7 +27,7 @@ from pydantic_core import core_schema
|
|
|
27
27
|
from trilogy.constants import (
|
|
28
28
|
MagicConstants,
|
|
29
29
|
)
|
|
30
|
-
from trilogy.core.enums import Ordering
|
|
30
|
+
from trilogy.core.enums import Modifier, Ordering
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class DataTyped(ABC):
|
|
@@ -211,9 +211,19 @@ class MapType(BaseModel):
|
|
|
211
211
|
return self.key_type
|
|
212
212
|
|
|
213
213
|
|
|
214
|
+
class StructComponent(BaseModel):
|
|
215
|
+
name: str
|
|
216
|
+
type: TYPEDEF_TYPES
|
|
217
|
+
modifiers: list[Modifier] = []
|
|
218
|
+
|
|
219
|
+
@field_validator("type", mode="plain")
|
|
220
|
+
def validate_Type(cls, v):
|
|
221
|
+
return v
|
|
222
|
+
|
|
223
|
+
|
|
214
224
|
class StructType(BaseModel):
|
|
215
|
-
fields: Sequence[TYPEDEF_TYPES]
|
|
216
|
-
fields_map: Dict[str, DataTyped | int | float | str]
|
|
225
|
+
fields: Sequence[StructComponent | TYPEDEF_TYPES]
|
|
226
|
+
fields_map: Dict[str, DataTyped | int | float | str | StructComponent]
|
|
217
227
|
|
|
218
228
|
@field_validator("fields", mode="plain")
|
|
219
229
|
def validate_type(cls, v):
|
|
@@ -234,6 +244,9 @@ class StructType(BaseModel):
|
|
|
234
244
|
def value(self):
|
|
235
245
|
return self.data_type.value
|
|
236
246
|
|
|
247
|
+
def __hash__(self):
|
|
248
|
+
return hash(str(self))
|
|
249
|
+
|
|
237
250
|
|
|
238
251
|
class ListWrapper(Generic[VT], UserList):
|
|
239
252
|
"""Used to distinguish parsed list objects from other lists"""
|
|
@@ -401,6 +414,8 @@ def arg_to_datatype(arg) -> CONCRETE_TYPES:
|
|
|
401
414
|
return DataType.STRING
|
|
402
415
|
elif isinstance(arg, float):
|
|
403
416
|
return DataType.FLOAT
|
|
417
|
+
elif isinstance(arg, DataType):
|
|
418
|
+
return arg
|
|
404
419
|
elif isinstance(arg, NumericType):
|
|
405
420
|
return arg
|
|
406
421
|
elif isinstance(arg, TraitDataType):
|
|
@@ -420,6 +435,8 @@ def arg_to_datatype(arg) -> CONCRETE_TYPES:
|
|
|
420
435
|
return DataType.DATETIME
|
|
421
436
|
elif isinstance(arg, date):
|
|
422
437
|
return DataType.DATE
|
|
438
|
+
elif isinstance(arg, StructComponent):
|
|
439
|
+
return arg_to_datatype(arg.type)
|
|
423
440
|
else:
|
|
424
441
|
raise ValueError(
|
|
425
442
|
f"Cannot parse arg datatype for arg of raw type {type(arg)} value {arg}"
|
trilogy/core/models/execute.py
CHANGED
|
@@ -1076,6 +1076,11 @@ class UnionCTE(BaseModel):
|
|
|
1076
1076
|
def group_to_grain(self) -> bool:
|
|
1077
1077
|
return False
|
|
1078
1078
|
|
|
1079
|
+
@property
|
|
1080
|
+
def group_concepts(self) -> List[BuildConcept]:
|
|
1081
|
+
# unions should always be on unique sets
|
|
1082
|
+
return []
|
|
1083
|
+
|
|
1079
1084
|
def __add__(self, other):
|
|
1080
1085
|
if not isinstance(other, UnionCTE) or not other.name == self.name:
|
|
1081
1086
|
raise SyntaxError("Cannot merge union CTEs")
|
trilogy/dialect/base.py
CHANGED
|
@@ -15,6 +15,7 @@ from trilogy.core.enums import (
|
|
|
15
15
|
ComparisonOperator,
|
|
16
16
|
DatePart,
|
|
17
17
|
FunctionType,
|
|
18
|
+
GroupMode,
|
|
18
19
|
Ordering,
|
|
19
20
|
ShowCategory,
|
|
20
21
|
UnnestMode,
|
|
@@ -186,6 +187,9 @@ FUNCTION_MAP = {
|
|
|
186
187
|
FunctionType.ARRAY: lambda x: f"[{', '.join(x)}]",
|
|
187
188
|
FunctionType.DATE_LITERAL: lambda x: f"date '{x}'",
|
|
188
189
|
FunctionType.DATETIME_LITERAL: lambda x: f"datetime '{x}'",
|
|
190
|
+
# MAP
|
|
191
|
+
FunctionType.MAP_KEYS: lambda x: f"map_keys({x[0]})",
|
|
192
|
+
FunctionType.MAP_VALUES: lambda x: f"map_values({x[0]})",
|
|
189
193
|
# ARRAY
|
|
190
194
|
FunctionType.ARRAY_SUM: lambda x: f"array_sum({x[0]})",
|
|
191
195
|
FunctionType.ARRAY_DISTINCT: lambda x: f"array_distinct({x[0]})",
|
|
@@ -338,6 +342,7 @@ class BaseDialect:
|
|
|
338
342
|
DATATYPE_MAP = DATATYPE_MAP
|
|
339
343
|
COMPLEX_DATATYPE_MAP = COMPLEX_DATATYPE_MAP
|
|
340
344
|
UNNEST_MODE = UnnestMode.CROSS_APPLY
|
|
345
|
+
GROUP_MODE = GroupMode.AUTO
|
|
341
346
|
|
|
342
347
|
def __init__(self, rendering: Rendering | None = None):
|
|
343
348
|
self.rendering = rendering or CONFIG.rendering
|
|
@@ -775,6 +780,33 @@ class BaseDialect:
|
|
|
775
780
|
else:
|
|
776
781
|
raise ValueError(f"Unable to render type {type(e)} {e}")
|
|
777
782
|
|
|
783
|
+
def render_cte_group_by(
|
|
784
|
+
self, cte: CTE | UnionCTE, select_columns
|
|
785
|
+
) -> Optional[list[str]]:
|
|
786
|
+
|
|
787
|
+
if not cte.group_to_grain:
|
|
788
|
+
return None
|
|
789
|
+
base = set(
|
|
790
|
+
[self.render_concept_sql(c, cte, alias=False) for c in cte.group_concepts]
|
|
791
|
+
)
|
|
792
|
+
if self.GROUP_MODE == GroupMode.AUTO:
|
|
793
|
+
return sorted(list(base))
|
|
794
|
+
|
|
795
|
+
else:
|
|
796
|
+
# find the index of each column in the select columns
|
|
797
|
+
final = []
|
|
798
|
+
found = []
|
|
799
|
+
for idx, c in enumerate(select_columns):
|
|
800
|
+
pre_alias = c.split(" as ")[0]
|
|
801
|
+
if pre_alias in base:
|
|
802
|
+
final.append(str(idx + 1))
|
|
803
|
+
found.append(pre_alias)
|
|
804
|
+
if not all(c in found for c in base):
|
|
805
|
+
raise ValueError(
|
|
806
|
+
f"Group by columns {base} not found in select columns {select_columns}"
|
|
807
|
+
)
|
|
808
|
+
return final
|
|
809
|
+
|
|
778
810
|
def render_cte(self, cte: CTE | UnionCTE, auto_sort: bool = True) -> CompiledCTE:
|
|
779
811
|
if isinstance(cte, UnionCTE):
|
|
780
812
|
base_statement = f"\n{cte.operator}\n".join(
|
|
@@ -926,20 +958,7 @@ class BaseDialect:
|
|
|
926
958
|
if cte.order_by
|
|
927
959
|
else None
|
|
928
960
|
),
|
|
929
|
-
group_by=(
|
|
930
|
-
sorted(
|
|
931
|
-
list(
|
|
932
|
-
set(
|
|
933
|
-
[
|
|
934
|
-
self.render_concept_sql(c, cte, alias=False)
|
|
935
|
-
for c in cte.group_concepts
|
|
936
|
-
]
|
|
937
|
-
)
|
|
938
|
-
)
|
|
939
|
-
)
|
|
940
|
-
if cte.group_to_grain
|
|
941
|
-
else None
|
|
942
|
-
),
|
|
961
|
+
group_by=self.render_cte_group_by(cte, select_columns),
|
|
943
962
|
),
|
|
944
963
|
)
|
|
945
964
|
|
trilogy/dialect/duckdb.py
CHANGED
|
@@ -76,6 +76,7 @@ FUNCTION_MAP = {
|
|
|
76
76
|
FunctionType.DATE: lambda x: f"cast({x[0]} as date)",
|
|
77
77
|
FunctionType.DATE_TRUNCATE: lambda x: f"date_trunc('{x[1]}', {x[0]})",
|
|
78
78
|
FunctionType.DATE_ADD: lambda x: f"date_add({x[0]}, {x[2]} * INTERVAL 1 {x[1]})",
|
|
79
|
+
FunctionType.DATE_SUB: lambda x: f"date_add({x[0]}, -{x[2]} * INTERVAL 1 {x[1]})",
|
|
79
80
|
FunctionType.DATE_PART: lambda x: f"date_part('{x[1]}', {x[0]})",
|
|
80
81
|
FunctionType.DATE_DIFF: lambda x: f"date_diff('{x[2]}', {x[0]}, {x[1]})",
|
|
81
82
|
FunctionType.CONCAT: lambda x: f"({' || '.join(x)})",
|
trilogy/dialect/presto.py
CHANGED
|
@@ -2,7 +2,7 @@ from typing import Any, Callable, Mapping
|
|
|
2
2
|
|
|
3
3
|
from jinja2 import Template
|
|
4
4
|
|
|
5
|
-
from trilogy.core.enums import FunctionType, UnnestMode, WindowType
|
|
5
|
+
from trilogy.core.enums import FunctionType, GroupMode, UnnestMode, WindowType
|
|
6
6
|
from trilogy.core.models.core import DataType
|
|
7
7
|
from trilogy.dialect.base import BaseDialect
|
|
8
8
|
|
|
@@ -94,6 +94,7 @@ class PrestoDialect(BaseDialect):
|
|
|
94
94
|
DataType.STRING: "VARCHAR",
|
|
95
95
|
}
|
|
96
96
|
UNNEST_MODE = UnnestMode.PRESTO
|
|
97
|
+
GROUP_MODE = GroupMode.BY_INDEX
|
|
97
98
|
|
|
98
99
|
|
|
99
100
|
class TrinoDialect(PrestoDialect):
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -89,6 +89,7 @@ from trilogy.core.models.core import (
|
|
|
89
89
|
MapType,
|
|
90
90
|
MapWrapper,
|
|
91
91
|
NumericType,
|
|
92
|
+
StructComponent,
|
|
92
93
|
StructType,
|
|
93
94
|
TraitDataType,
|
|
94
95
|
TupleWrapper,
|
|
@@ -443,20 +444,39 @@ class ParseToObjects(Transformer):
|
|
|
443
444
|
def MINUS(self, args) -> str:
|
|
444
445
|
return "-"
|
|
445
446
|
|
|
447
|
+
@v_args(meta=True)
|
|
448
|
+
def struct_component(self, meta: Meta, args) -> StructComponent:
|
|
449
|
+
modifiers = []
|
|
450
|
+
for arg in args:
|
|
451
|
+
if isinstance(arg, Modifier):
|
|
452
|
+
modifiers.append(arg)
|
|
453
|
+
return StructComponent(name=args[0], type=args[1], modifiers=modifiers)
|
|
454
|
+
|
|
446
455
|
@v_args(meta=True)
|
|
447
456
|
def struct_type(self, meta: Meta, args) -> StructType:
|
|
448
457
|
final: list[
|
|
449
|
-
DataType
|
|
458
|
+
DataType
|
|
459
|
+
| MapType
|
|
460
|
+
| ArrayType
|
|
461
|
+
| NumericType
|
|
462
|
+
| StructType
|
|
463
|
+
| StructComponent
|
|
464
|
+
| Concept
|
|
450
465
|
] = []
|
|
451
466
|
for arg in args:
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
467
|
+
if isinstance(arg, StructComponent):
|
|
468
|
+
final.append(arg)
|
|
469
|
+
else:
|
|
470
|
+
new = self.environment.concepts.__getitem__( # type: ignore
|
|
471
|
+
key=arg, line_no=meta.line
|
|
472
|
+
)
|
|
473
|
+
final.append(new)
|
|
456
474
|
|
|
457
475
|
return StructType(
|
|
458
476
|
fields=final,
|
|
459
|
-
fields_map={
|
|
477
|
+
fields_map={
|
|
478
|
+
x.name: x for x in final if isinstance(x, (Concept, StructComponent))
|
|
479
|
+
},
|
|
460
480
|
)
|
|
461
481
|
|
|
462
482
|
def list_type(self, args) -> ArrayType:
|
|
@@ -683,7 +703,9 @@ class ParseToObjects(Transformer):
|
|
|
683
703
|
)
|
|
684
704
|
# let constant purposes exist to support round-tripping
|
|
685
705
|
# as a build concept may end up with a constant based on constant inlining happening recursively
|
|
686
|
-
if
|
|
706
|
+
if purpose == Purpose.KEY and concept.purpose != Purpose.KEY:
|
|
707
|
+
concept.purpose = Purpose.KEY
|
|
708
|
+
elif (
|
|
687
709
|
purpose
|
|
688
710
|
and purpose != Purpose.AUTO
|
|
689
711
|
and concept.purpose != purpose
|
|
@@ -2024,6 +2046,16 @@ class ParseToObjects(Transformer):
|
|
|
2024
2046
|
def fbool(self, meta, args):
|
|
2025
2047
|
return self.function_factory.create_function(args, FunctionType.BOOL, meta)
|
|
2026
2048
|
|
|
2049
|
+
@v_args(meta=True)
|
|
2050
|
+
def fmap_keys(self, meta, args):
|
|
2051
|
+
return self.function_factory.create_function(args, FunctionType.MAP_KEYS, meta)
|
|
2052
|
+
|
|
2053
|
+
@v_args(meta=True)
|
|
2054
|
+
def fmap_values(self, meta, args):
|
|
2055
|
+
return self.function_factory.create_function(
|
|
2056
|
+
args, FunctionType.MAP_VALUES, meta
|
|
2057
|
+
)
|
|
2058
|
+
|
|
2027
2059
|
@v_args(meta=True)
|
|
2028
2060
|
def farray_sum(self, meta, args):
|
|
2029
2061
|
return self.function_factory.create_function(args, FunctionType.ARRAY_SUM, meta)
|
trilogy/parsing/trilogy.lark
CHANGED
|
@@ -181,7 +181,8 @@
|
|
|
181
181
|
//indexing into an expression is a function
|
|
182
182
|
index_access: atom "[" int_lit "]"
|
|
183
183
|
map_key_access: atom "[" string_lit "]"
|
|
184
|
-
|
|
184
|
+
_ATTR_ACCESS.1 : "getattr("i
|
|
185
|
+
attr_access: ( _ATTR_ACCESS atom "," string_lit ")") | (atom "." string_lit)
|
|
185
186
|
|
|
186
187
|
?expr: comparison_root | between_root
|
|
187
188
|
|
|
@@ -309,6 +310,14 @@
|
|
|
309
310
|
farray_transform: _ARRAY_TRANSFORM expr "," transform_lambda ")"
|
|
310
311
|
|
|
311
312
|
_array_functions: farray_sum | farray_distinct | farray_sort | farray_transform | farray_to_string
|
|
313
|
+
|
|
314
|
+
//map_functions
|
|
315
|
+
_MAP_KEYS.1: "map_keys("i
|
|
316
|
+
fmap_keys: _MAP_KEYS expr ")"
|
|
317
|
+
_MAP_VALUES.1: "map_values("i
|
|
318
|
+
fmap_values: _MAP_VALUES expr ")"
|
|
319
|
+
|
|
320
|
+
_map_functions: fmap_keys | fmap_values
|
|
312
321
|
|
|
313
322
|
// special aggregate
|
|
314
323
|
_GROUP.1: "group("i
|
|
@@ -367,7 +376,7 @@
|
|
|
367
376
|
fyear: _YEAR expr ")"
|
|
368
377
|
|
|
369
378
|
DATE_PART: "DAY"i | "WEEK"i | "MONTH"i | "QUARTER"i | "YEAR"i | "MINUTE"i | "HOUR"i | "SECOND"i | "DAY_OF_WEEK"i
|
|
370
|
-
_DATE_TRUNC.1: "date_trunc("i
|
|
379
|
+
_DATE_TRUNC.1: "date_trunc("i | "date_truncate("i
|
|
371
380
|
fdate_trunc: _DATE_TRUNC expr "," DATE_PART ")"
|
|
372
381
|
_DATE_PART.1: "date_part("i
|
|
373
382
|
fdate_part: _DATE_PART expr "," DATE_PART ")"
|
|
@@ -380,7 +389,7 @@
|
|
|
380
389
|
|
|
381
390
|
_date_functions: fdate | fdate_add | fdate_sub | fdate_diff | fdatetime | ftimestamp | fsecond | fminute | fhour | fday | fday_of_week | fweek | fmonth | fquarter | fyear | fdate_part | fdate_trunc
|
|
382
391
|
|
|
383
|
-
_static_functions: _string_functions | _math_functions | _array_functions
|
|
392
|
+
_static_functions: _string_functions | _math_functions | _array_functions | _map_functions
|
|
384
393
|
|
|
385
394
|
custom_function: "@" IDENTIFIER "(" (expr ",")* expr ")"
|
|
386
395
|
|
|
@@ -424,8 +433,8 @@
|
|
|
424
433
|
MODIFIER: /OPTIONAL|PARTIAL|NULLABLE/i
|
|
425
434
|
|
|
426
435
|
SHORTHAND_MODIFIER: /~|\?/
|
|
427
|
-
|
|
428
|
-
struct_type: "struct"i "<" ((
|
|
436
|
+
struct_component: IDENTIFIER ":" data_type concept_nullable_modifier? metadata?
|
|
437
|
+
struct_type: "struct"i "<" ((struct_component | IDENTIFIER) ",")* (struct_component | IDENTIFIER) ","? ">"
|
|
429
438
|
|
|
430
439
|
list_type: ("list"i | "array"i) "<" (data_type | IDENTIFIER) ">"
|
|
431
440
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|