pytrilogy 0.0.3.73__py3-none-any.whl → 0.0.3.75__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.73.dist-info → pytrilogy-0.0.3.75.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.3.73.dist-info → pytrilogy-0.0.3.75.dist-info}/RECORD +19 -19
- trilogy/__init__.py +1 -1
- trilogy/authoring/__init__.py +2 -2
- trilogy/core/enums.py +8 -0
- trilogy/core/functions.py +56 -9
- trilogy/core/models/author.py +20 -11
- trilogy/core/models/build.py +14 -5
- trilogy/core/models/core.py +13 -11
- trilogy/core/processing/utility.py +2 -2
- trilogy/dialect/base.py +20 -6
- trilogy/dialect/duckdb.py +18 -0
- trilogy/parsing/parse_engine.py +62 -9
- trilogy/parsing/render.py +2 -2
- trilogy/parsing/trilogy.lark +19 -4
- {pytrilogy-0.0.3.73.dist-info → pytrilogy-0.0.3.75.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.73.dist-info → pytrilogy-0.0.3.75.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.73.dist-info → pytrilogy-0.0.3.75.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.73.dist-info → pytrilogy-0.0.3.75.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.75.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
2
|
+
trilogy/__init__.py,sha256=MIf9sLTcNq9Rl4yjZRDALK9URcm1vSxXzixHDnfPJg0,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
|
|
@@ -8,25 +8,25 @@ trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
|
|
|
8
8
|
trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
trilogy/render.py,sha256=qQWwduymauOlB517UtM-VGbVe8Cswa4UJub5aGbSO6c,1512
|
|
10
10
|
trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
|
|
11
|
-
trilogy/authoring/__init__.py,sha256=
|
|
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=--iG8Q1NJ9JBJe4IIdO37P2T3TOWn_CmqbagsQGfNf0,8050
|
|
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=wQ5yCvMKXSCkaSo7yiwlGY0wvqE4Sm2MI9e5NEr2ORw,30933
|
|
20
20
|
trilogy/core/graph_models.py,sha256=BYhJzHKSgnZHVLJs1CfsgrxTPHqKqPNeA64RlozGY0A,3498
|
|
21
21
|
trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
|
|
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=iCj5zQcBYB-4u93vM9OfX56-eqRcWQhKRAwrHXRsNGA,78046
|
|
27
|
+
trilogy/core/models/build.py,sha256=UfWuG86O8yivqK4UhCOXk5IyHUw0vZPckcSljeCuK3o,65859
|
|
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=q-8ExAaSB3PoSDX2XOqfZ01HBBvT1pZFs7jGg1vZizc,11096
|
|
30
30
|
trilogy/core/models/datasource.py,sha256=wogTevZ-9CyUW2a8gjzqMCieircxi-J5lkI7EOAZnck,9596
|
|
31
31
|
trilogy/core/models/environment.py,sha256=TBbPfsXHpJK49QKuqHwhgZD4PwHiSAYjXmTTTomRE7o,27861
|
|
32
32
|
trilogy/core/models/execute.py,sha256=Erufst3yroU3PgNJMWF5lICm14AkH02laYeesSB01UU,41705
|
|
@@ -41,7 +41,7 @@ trilogy/core/processing/discovery_node_factory.py,sha256=I3JJxoF-u8OVvqXXAOhvMg2
|
|
|
41
41
|
trilogy/core/processing/discovery_utility.py,sha256=3xdd1ypKappSDm0SJs7WtW5YegL80SlYhDQlkNePp4E,4549
|
|
42
42
|
trilogy/core/processing/discovery_validation.py,sha256=fGWJmKpgEd1f4RkK-fYOBUT1cwsJnahwXFAdRlou7MI,5365
|
|
43
43
|
trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuXl-Te85lag,1205
|
|
44
|
-
trilogy/core/processing/utility.py,sha256=
|
|
44
|
+
trilogy/core/processing/utility.py,sha256=xsuVMRFi2nY2So0yohhweI2D92wZVsTpHezS0giM4ck,22583
|
|
45
45
|
trilogy/core/processing/node_generators/__init__.py,sha256=w8TQQgNhyAra6JQHdg1_Ags4BGyxjXYruu6UeC5yOkI,873
|
|
46
46
|
trilogy/core/processing/node_generators/basic_node.py,sha256=luN8LftafZepoFgDRv4gmvEGFlOI2j0icJ5fz4UT7uo,5165
|
|
47
47
|
trilogy/core/processing/node_generators/common.py,sha256=PdysdroW9DUADP7f5Wv_GKPUyCTROZV1g3L45fawxi8,9443
|
|
@@ -76,12 +76,12 @@ trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
|
76
76
|
trilogy/core/statements/common.py,sha256=KxEmz2ySySyZ6CTPzn0fJl5NX2KOk1RPyuUSwWhnK1g,759
|
|
77
77
|
trilogy/core/statements/execute.py,sha256=rqfuoMuXPcH7L7TmE1dSiZ_K_A1ohB8whVMfGimZBOk,1294
|
|
78
78
|
trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
79
|
-
trilogy/dialect/base.py,sha256=
|
|
79
|
+
trilogy/dialect/base.py,sha256=fEkPou4Eku7Un9NLZ6ya8OIVdaaV8ZJyZIbn56xOrVc,45829
|
|
80
80
|
trilogy/dialect/bigquery.py,sha256=8xhEu0z_lKANjbvzvBbC7CeKrJf1iP8YyrHqNale-ug,4351
|
|
81
81
|
trilogy/dialect/common.py,sha256=tSthIZOXXRPQ4KeMKnDDsH7KlTmf2EVqigVtLyoc4zc,6071
|
|
82
82
|
trilogy/dialect/config.py,sha256=olnyeVU5W5T6b9-dMeNAnvxuPlyc2uefb7FRME094Ec,3834
|
|
83
83
|
trilogy/dialect/dataframe.py,sha256=RUbNgReEa9g3pL6H7fP9lPTrAij5pkqedpZ99D8_5AE,1522
|
|
84
|
-
trilogy/dialect/duckdb.py,sha256=
|
|
84
|
+
trilogy/dialect/duckdb.py,sha256=0qWQkyijz1_MnsVFyY_Iqqx0ba6E8o50s8pvLLpZH6g,5138
|
|
85
85
|
trilogy/dialect/enums.py,sha256=FRNYQ5-w-B6-X0yXKNU5g9GowsMlERFogTC5u2nxL_s,4740
|
|
86
86
|
trilogy/dialect/postgres.py,sha256=el2PKwfyvWGk5EZtLudqAH5ewLitY1sFHJiocBSyxyM,3393
|
|
87
87
|
trilogy/dialect/presto.py,sha256=yzSF8SZ6o1dizj1UueAa7S3lR0qNYJdSXbF78EHyhY0,3668
|
|
@@ -97,9 +97,9 @@ trilogy/parsing/common.py,sha256=yV1AckK0h8u1OFeGQBTMu-wuW5m63c5CcZuPicsTH_w,306
|
|
|
97
97
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
98
98
|
trilogy/parsing/exceptions.py,sha256=Xwwsv2C9kSNv2q-HrrKC1f60JNHShXcCMzstTSEbiCw,154
|
|
99
99
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
100
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
101
|
-
trilogy/parsing/render.py,sha256=
|
|
102
|
-
trilogy/parsing/trilogy.lark,sha256=
|
|
100
|
+
trilogy/parsing/parse_engine.py,sha256=XQa8kSLiXd--MH89HvwlyNClQnFJ7ZLkm8PwxMsxHeY,74587
|
|
101
|
+
trilogy/parsing/render.py,sha256=HSNntD82GiiwHT-TWPLXAaIMWLYVV5B5zQEsgwrHIBE,19605
|
|
102
|
+
trilogy/parsing/trilogy.lark,sha256=v2UqMBhU9hn9km4mvQkk3E2wsEZjJ4cz4wU72NFFvU4,14993
|
|
103
103
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
104
104
|
trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
|
|
105
105
|
trilogy/std/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -110,8 +110,8 @@ trilogy/std/money.preql,sha256=XWwvAV3WxBsHX9zfptoYRnBigcfYwrYtBHXTME0xJuQ,2082
|
|
|
110
110
|
trilogy/std/net.preql,sha256=7l7MqIjs6TDCpO6dBAoNJU81Ex255jZRK36kBgE1GDs,158
|
|
111
111
|
trilogy/std/ranking.preql,sha256=LDoZrYyz4g3xsII9XwXfmstZD-_92i1Eox1UqkBIfi8,83
|
|
112
112
|
trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
|
|
113
|
-
pytrilogy-0.0.3.
|
|
114
|
-
pytrilogy-0.0.3.
|
|
115
|
-
pytrilogy-0.0.3.
|
|
116
|
-
pytrilogy-0.0.3.
|
|
117
|
-
pytrilogy-0.0.3.
|
|
113
|
+
pytrilogy-0.0.3.75.dist-info/METADATA,sha256=BiUDu-P-eT6dmtLstg2EmFFJ0Pc-eU6k17U--NmkXqU,9734
|
|
114
|
+
pytrilogy-0.0.3.75.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
115
|
+
pytrilogy-0.0.3.75.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
|
|
116
|
+
pytrilogy-0.0.3.75.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
117
|
+
pytrilogy-0.0.3.75.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/authoring/__init__.py
CHANGED
|
@@ -37,8 +37,8 @@ from trilogy.core.models.author import (
|
|
|
37
37
|
WindowType,
|
|
38
38
|
)
|
|
39
39
|
from trilogy.core.models.core import (
|
|
40
|
+
ArrayType,
|
|
40
41
|
DataType,
|
|
41
|
-
ListType,
|
|
42
42
|
ListWrapper,
|
|
43
43
|
MapType,
|
|
44
44
|
StructType,
|
|
@@ -77,7 +77,7 @@ __all__ = [
|
|
|
77
77
|
"WindowItemOver",
|
|
78
78
|
"DataType",
|
|
79
79
|
"StructType",
|
|
80
|
-
"
|
|
80
|
+
"ArrayType",
|
|
81
81
|
"Grain",
|
|
82
82
|
"RowsetDerivationStatement",
|
|
83
83
|
"MapType",
|
trilogy/core/enums.py
CHANGED
|
@@ -149,6 +149,12 @@ class FunctionType(Enum):
|
|
|
149
149
|
DATE_LITERAL = "date_literal"
|
|
150
150
|
DATETIME_LITERAL = "datetime_literal"
|
|
151
151
|
|
|
152
|
+
# ARRAY
|
|
153
|
+
ARRAY_DISTINCT = "array_distinct"
|
|
154
|
+
ARRAY_SUM = "array_sum"
|
|
155
|
+
ARRAY_SORT = "array_sort"
|
|
156
|
+
ARRAY_TRANSFORM = "array_transform"
|
|
157
|
+
|
|
152
158
|
# TEXT AND MAYBE MORE
|
|
153
159
|
SPLIT = "split"
|
|
154
160
|
LENGTH = "len"
|
|
@@ -177,6 +183,7 @@ class FunctionType(Enum):
|
|
|
177
183
|
MAX = "max"
|
|
178
184
|
MIN = "min"
|
|
179
185
|
AVG = "avg"
|
|
186
|
+
ARRAY_AGG = "array_agg"
|
|
180
187
|
|
|
181
188
|
# String
|
|
182
189
|
LIKE = "like"
|
|
@@ -228,6 +235,7 @@ class FunctionClass(Enum):
|
|
|
228
235
|
FunctionType.MIN,
|
|
229
236
|
FunctionType.SUM,
|
|
230
237
|
FunctionType.AVG,
|
|
238
|
+
FunctionType.ARRAY_AGG,
|
|
231
239
|
FunctionType.COUNT,
|
|
232
240
|
FunctionType.COUNT_DISTINCT,
|
|
233
241
|
]
|
trilogy/core/functions.py
CHANGED
|
@@ -25,8 +25,8 @@ from trilogy.core.models.author import (
|
|
|
25
25
|
)
|
|
26
26
|
from trilogy.core.models.core import (
|
|
27
27
|
CONCRETE_TYPES,
|
|
28
|
+
ArrayType,
|
|
28
29
|
DataType,
|
|
29
|
-
ListType,
|
|
30
30
|
MapType,
|
|
31
31
|
NumericType,
|
|
32
32
|
StructType,
|
|
@@ -45,14 +45,14 @@ class FunctionConfig:
|
|
|
45
45
|
valid_inputs: set[DataType] | list[set[DataType]] | None = None
|
|
46
46
|
output_purpose: Purpose | None = None
|
|
47
47
|
output_type: (
|
|
48
|
-
DataType |
|
|
48
|
+
DataType | ArrayType | MapType | StructType | NumericType | TraitDataType | None
|
|
49
49
|
) = None
|
|
50
50
|
output_type_function: Optional[Callable] = None
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
def get_unnest_output_type(args: list[Any]) -> CONCRETE_TYPES:
|
|
54
54
|
output = arg_to_datatype(args[0])
|
|
55
|
-
if isinstance(output, (
|
|
55
|
+
if isinstance(output, (ArrayType, MapType)):
|
|
56
56
|
output = output.value_data_type
|
|
57
57
|
else:
|
|
58
58
|
output = DataType.STRING
|
|
@@ -69,12 +69,16 @@ def get_coalesce_output_type(args: list[Any]) -> CONCRETE_TYPES:
|
|
|
69
69
|
return processed[0]
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
def get_transform_output_type(args: list[Any]) -> CONCRETE_TYPES:
|
|
73
|
+
return arg_to_datatype(args[2])
|
|
74
|
+
|
|
75
|
+
|
|
72
76
|
def get_index_output_type(
|
|
73
77
|
args: list[Any],
|
|
74
78
|
) -> CONCRETE_TYPES:
|
|
75
79
|
arg = args[0]
|
|
76
80
|
datatype = arg_to_datatype(arg)
|
|
77
|
-
if isinstance(datatype,
|
|
81
|
+
if isinstance(datatype, ArrayType):
|
|
78
82
|
return datatype.value_data_type
|
|
79
83
|
elif isinstance(datatype, MapType):
|
|
80
84
|
return datatype.value_data_type
|
|
@@ -184,7 +188,6 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
184
188
|
FunctionType.UNNEST: FunctionConfig(
|
|
185
189
|
valid_inputs={
|
|
186
190
|
DataType.ARRAY,
|
|
187
|
-
DataType.LIST,
|
|
188
191
|
},
|
|
189
192
|
output_purpose=Purpose.KEY,
|
|
190
193
|
output_type_function=get_unnest_output_type,
|
|
@@ -235,13 +238,12 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
235
238
|
FunctionType.SPLIT: FunctionConfig(
|
|
236
239
|
valid_inputs={DataType.STRING},
|
|
237
240
|
output_purpose=Purpose.PROPERTY,
|
|
238
|
-
output_type=
|
|
241
|
+
output_type=ArrayType(type=DataType.STRING),
|
|
239
242
|
arg_count=2,
|
|
240
243
|
),
|
|
241
244
|
FunctionType.INDEX_ACCESS: FunctionConfig(
|
|
242
245
|
valid_inputs=[
|
|
243
246
|
{
|
|
244
|
-
DataType.LIST,
|
|
245
247
|
DataType.ARRAY,
|
|
246
248
|
},
|
|
247
249
|
{
|
|
@@ -252,6 +254,43 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
252
254
|
output_type_function=get_index_output_type,
|
|
253
255
|
arg_count=2,
|
|
254
256
|
),
|
|
257
|
+
FunctionType.ARRAY_DISTINCT: FunctionConfig(
|
|
258
|
+
valid_inputs={
|
|
259
|
+
DataType.ARRAY,
|
|
260
|
+
},
|
|
261
|
+
output_purpose=Purpose.PROPERTY,
|
|
262
|
+
output_type_function=lambda args: get_output_type_at_index(args, 0),
|
|
263
|
+
arg_count=1,
|
|
264
|
+
),
|
|
265
|
+
FunctionType.ARRAY_SORT: FunctionConfig(
|
|
266
|
+
valid_inputs=[
|
|
267
|
+
{DataType.ARRAY},
|
|
268
|
+
{DataType.STRING},
|
|
269
|
+
],
|
|
270
|
+
output_purpose=Purpose.PROPERTY,
|
|
271
|
+
output_type_function=lambda args: get_output_type_at_index(args, 0),
|
|
272
|
+
arg_count=2,
|
|
273
|
+
),
|
|
274
|
+
FunctionType.ARRAY_TRANSFORM: FunctionConfig(
|
|
275
|
+
valid_inputs=[
|
|
276
|
+
{
|
|
277
|
+
DataType.ARRAY,
|
|
278
|
+
},
|
|
279
|
+
{*DataType},
|
|
280
|
+
{*DataType},
|
|
281
|
+
],
|
|
282
|
+
output_purpose=Purpose.PROPERTY,
|
|
283
|
+
output_type_function=get_transform_output_type,
|
|
284
|
+
arg_count=3,
|
|
285
|
+
),
|
|
286
|
+
FunctionType.ARRAY_SUM: FunctionConfig(
|
|
287
|
+
valid_inputs={
|
|
288
|
+
DataType.ARRAY,
|
|
289
|
+
},
|
|
290
|
+
output_purpose=Purpose.PROPERTY,
|
|
291
|
+
output_type_function=get_index_output_type,
|
|
292
|
+
arg_count=1,
|
|
293
|
+
),
|
|
255
294
|
FunctionType.MAP_ACCESS: FunctionConfig(
|
|
256
295
|
valid_inputs=[
|
|
257
296
|
{
|
|
@@ -722,7 +761,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
722
761
|
FunctionType.ARRAY: FunctionConfig(
|
|
723
762
|
output_purpose=Purpose.PROPERTY,
|
|
724
763
|
arg_count=InfiniteFunctionArgs,
|
|
725
|
-
output_type=
|
|
764
|
+
output_type=ArrayType(type=DataType.STRING),
|
|
726
765
|
),
|
|
727
766
|
FunctionType.LENGTH: FunctionConfig(
|
|
728
767
|
valid_inputs={DataType.STRING, DataType.ARRAY, DataType.MAP},
|
|
@@ -740,6 +779,14 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
740
779
|
output_purpose=Purpose.METRIC,
|
|
741
780
|
arg_count=1,
|
|
742
781
|
),
|
|
782
|
+
FunctionType.ARRAY_AGG: FunctionConfig(
|
|
783
|
+
valid_inputs={*DataType},
|
|
784
|
+
output_purpose=Purpose.METRIC,
|
|
785
|
+
output_type_function=lambda args: ArrayType(
|
|
786
|
+
type=merge_datatypes([arg_to_datatype(x) for x in args])
|
|
787
|
+
),
|
|
788
|
+
arg_count=1,
|
|
789
|
+
),
|
|
743
790
|
FunctionType.AVG: FunctionConfig(
|
|
744
791
|
valid_inputs={
|
|
745
792
|
DataType.INTEGER,
|
|
@@ -837,7 +884,7 @@ def create_function_derived_concept(
|
|
|
837
884
|
arguments: list[Concept],
|
|
838
885
|
environment: Environment,
|
|
839
886
|
output_type: Optional[
|
|
840
|
-
DataType |
|
|
887
|
+
DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
|
|
841
888
|
] = None,
|
|
842
889
|
output_purpose: Optional[Purpose] = None,
|
|
843
890
|
) -> Concept:
|
trilogy/core/models/author.py
CHANGED
|
@@ -48,9 +48,9 @@ from trilogy.core.enums import (
|
|
|
48
48
|
)
|
|
49
49
|
from trilogy.core.models.core import (
|
|
50
50
|
Addressable,
|
|
51
|
+
ArrayType,
|
|
51
52
|
DataType,
|
|
52
53
|
DataTyped,
|
|
53
|
-
ListType,
|
|
54
54
|
ListWrapper,
|
|
55
55
|
MapType,
|
|
56
56
|
MapWrapper,
|
|
@@ -104,7 +104,7 @@ class HasUUID(ABC):
|
|
|
104
104
|
class ConceptRef(Addressable, Namespaced, DataTyped, Mergeable, BaseModel):
|
|
105
105
|
address: str
|
|
106
106
|
datatype: (
|
|
107
|
-
DataType | TraitDataType |
|
|
107
|
+
DataType | TraitDataType | ArrayType | StructType | MapType | NumericType
|
|
108
108
|
) = DataType.UNKNOWN
|
|
109
109
|
metadata: Optional["Metadata"] = None
|
|
110
110
|
|
|
@@ -646,7 +646,7 @@ class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
|
|
|
646
646
|
)
|
|
647
647
|
elif self.operator in (ComparisonOperator.IN, ComparisonOperator.NOT_IN):
|
|
648
648
|
right_type = arg_to_datatype(self.right)
|
|
649
|
-
if isinstance(right_type,
|
|
649
|
+
if isinstance(right_type, ArrayType) and not is_compatible_datatype(
|
|
650
650
|
arg_to_datatype(self.left), right_type.value_data_type
|
|
651
651
|
):
|
|
652
652
|
raise SyntaxError(
|
|
@@ -802,7 +802,7 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
802
802
|
extra="forbid",
|
|
803
803
|
)
|
|
804
804
|
name: str
|
|
805
|
-
datatype: DataType | TraitDataType |
|
|
805
|
+
datatype: DataType | TraitDataType | ArrayType | StructType | MapType | NumericType
|
|
806
806
|
purpose: Purpose
|
|
807
807
|
derivation: Derivation = Derivation.ROOT
|
|
808
808
|
granularity: Granularity = Granularity.MULTI_ROW
|
|
@@ -1277,7 +1277,7 @@ class UndefinedConceptFull(Concept, Mergeable, Namespaced):
|
|
|
1277
1277
|
name: str
|
|
1278
1278
|
line_no: int | None = None
|
|
1279
1279
|
datatype: (
|
|
1280
|
-
DataType | TraitDataType |
|
|
1280
|
+
DataType | TraitDataType | ArrayType | StructType | MapType | NumericType
|
|
1281
1281
|
) = DataType.UNKNOWN
|
|
1282
1282
|
purpose: Purpose = Purpose.UNKNOWN
|
|
1283
1283
|
|
|
@@ -1437,10 +1437,10 @@ class WindowItem(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1437
1437
|
|
|
1438
1438
|
|
|
1439
1439
|
def get_basic_type(
|
|
1440
|
-
type: DataType |
|
|
1440
|
+
type: DataType | ArrayType | StructType | MapType | NumericType | TraitDataType,
|
|
1441
1441
|
) -> DataType:
|
|
1442
|
-
if isinstance(type,
|
|
1443
|
-
return DataType.
|
|
1442
|
+
if isinstance(type, ArrayType):
|
|
1443
|
+
return DataType.ARRAY
|
|
1444
1444
|
if isinstance(type, StructType):
|
|
1445
1445
|
return DataType.STRUCT
|
|
1446
1446
|
if isinstance(type, MapType):
|
|
@@ -1609,7 +1609,7 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1609
1609
|
operator: FunctionType
|
|
1610
1610
|
arg_count: int = Field(default=1)
|
|
1611
1611
|
output_datatype: (
|
|
1612
|
-
DataType |
|
|
1612
|
+
DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
|
|
1613
1613
|
)
|
|
1614
1614
|
output_purpose: Purpose
|
|
1615
1615
|
valid_inputs: Optional[
|
|
@@ -2358,9 +2358,10 @@ class Comment(BaseModel):
|
|
|
2358
2358
|
text: str
|
|
2359
2359
|
|
|
2360
2360
|
|
|
2361
|
-
class ArgBinding(Namespaced, BaseModel):
|
|
2361
|
+
class ArgBinding(Namespaced, DataTyped, BaseModel):
|
|
2362
2362
|
name: str
|
|
2363
2363
|
default: Expr | None = None
|
|
2364
|
+
datatype: DataType = DataType.UNKNOWN
|
|
2364
2365
|
|
|
2365
2366
|
def with_namespace(self, namespace):
|
|
2366
2367
|
return ArgBinding(
|
|
@@ -2372,6 +2373,12 @@ class ArgBinding(Namespaced, BaseModel):
|
|
|
2372
2373
|
),
|
|
2373
2374
|
)
|
|
2374
2375
|
|
|
2376
|
+
@property
|
|
2377
|
+
def output_datatype(self):
|
|
2378
|
+
if self.default is not None:
|
|
2379
|
+
return arg_to_datatype(self.default)
|
|
2380
|
+
return self.datatype
|
|
2381
|
+
|
|
2375
2382
|
|
|
2376
2383
|
class CustomType(BaseModel):
|
|
2377
2384
|
name: str
|
|
@@ -2426,7 +2433,7 @@ FuncArgs = (
|
|
|
2426
2433
|
| MapWrapper[Any, Any]
|
|
2427
2434
|
| TraitDataType
|
|
2428
2435
|
| DataType
|
|
2429
|
-
|
|
|
2436
|
+
| ArrayType
|
|
2430
2437
|
| MapType
|
|
2431
2438
|
| NumericType
|
|
2432
2439
|
| ListWrapper[Any]
|
|
@@ -2434,4 +2441,6 @@ FuncArgs = (
|
|
|
2434
2441
|
| Comparison
|
|
2435
2442
|
| Conditional
|
|
2436
2443
|
| MagicConstants
|
|
2444
|
+
| ArgBinding
|
|
2445
|
+
| Ordering
|
|
2437
2446
|
)
|
trilogy/core/models/build.py
CHANGED
|
@@ -45,6 +45,7 @@ from trilogy.core.models.author import (
|
|
|
45
45
|
AggregateWrapper,
|
|
46
46
|
AlignClause,
|
|
47
47
|
AlignItem,
|
|
48
|
+
ArgBinding,
|
|
48
49
|
CaseElse,
|
|
49
50
|
CaseWhen,
|
|
50
51
|
Comparison,
|
|
@@ -72,9 +73,9 @@ from trilogy.core.models.author import (
|
|
|
72
73
|
)
|
|
73
74
|
from trilogy.core.models.core import (
|
|
74
75
|
Addressable,
|
|
76
|
+
ArrayType,
|
|
75
77
|
DataType,
|
|
76
78
|
DataTyped,
|
|
77
|
-
ListType,
|
|
78
79
|
ListWrapper,
|
|
79
80
|
MapType,
|
|
80
81
|
MapWrapper,
|
|
@@ -791,7 +792,7 @@ class BuildSubselectComparison(BuildComparison):
|
|
|
791
792
|
class BuildConcept(Addressable, BuildConceptArgs, DataTyped, BaseModel):
|
|
792
793
|
model_config = ConfigDict(extra="forbid")
|
|
793
794
|
name: str
|
|
794
|
-
datatype: DataType |
|
|
795
|
+
datatype: DataType | ArrayType | StructType | MapType | NumericType
|
|
795
796
|
purpose: Purpose
|
|
796
797
|
build_is_aggregate: bool
|
|
797
798
|
derivation: Derivation = Derivation.ROOT
|
|
@@ -1083,7 +1084,7 @@ class BuildFunction(DataTyped, BuildConceptArgs, BaseModel):
|
|
|
1083
1084
|
operator: FunctionType
|
|
1084
1085
|
arg_count: int = Field(default=1)
|
|
1085
1086
|
output_datatype: (
|
|
1086
|
-
DataType |
|
|
1087
|
+
DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
|
|
1087
1088
|
)
|
|
1088
1089
|
output_purpose: Purpose
|
|
1089
1090
|
valid_inputs: Optional[
|
|
@@ -1102,7 +1103,7 @@ class BuildFunction(DataTyped, BuildConceptArgs, BaseModel):
|
|
|
1102
1103
|
MapWrapper[Any, Any],
|
|
1103
1104
|
TraitDataType,
|
|
1104
1105
|
DataType,
|
|
1105
|
-
|
|
1106
|
+
ArrayType,
|
|
1106
1107
|
MapType,
|
|
1107
1108
|
NumericType,
|
|
1108
1109
|
DatePart,
|
|
@@ -2075,7 +2076,7 @@ class Factory:
|
|
|
2075
2076
|
return base
|
|
2076
2077
|
|
|
2077
2078
|
@build.register
|
|
2078
|
-
def _(self, base:
|
|
2079
|
+
def _(self, base: ArrayType):
|
|
2079
2080
|
return base
|
|
2080
2081
|
|
|
2081
2082
|
@build.register
|
|
@@ -2086,6 +2087,14 @@ class Factory:
|
|
|
2086
2087
|
def _(self, base: MapType):
|
|
2087
2088
|
return base
|
|
2088
2089
|
|
|
2090
|
+
@build.register
|
|
2091
|
+
def _(self, base: ArgBinding):
|
|
2092
|
+
return base
|
|
2093
|
+
|
|
2094
|
+
@build.register
|
|
2095
|
+
def _(self, base: Ordering):
|
|
2096
|
+
return base
|
|
2097
|
+
|
|
2089
2098
|
@build.register
|
|
2090
2099
|
def _(self, base: Datasource):
|
|
2091
2100
|
local_cache: dict[str, BuildConcept] = {}
|
trilogy/core/models/core.py
CHANGED
|
@@ -27,6 +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
31
|
|
|
31
32
|
|
|
32
33
|
class DataTyped(ABC):
|
|
@@ -53,7 +54,7 @@ class Addressable(ABC):
|
|
|
53
54
|
TYPEDEF_TYPES = Union[
|
|
54
55
|
"DataType",
|
|
55
56
|
"MapType",
|
|
56
|
-
"
|
|
57
|
+
"ArrayType",
|
|
57
58
|
"NumericType",
|
|
58
59
|
"StructType",
|
|
59
60
|
"DataTyped",
|
|
@@ -63,7 +64,7 @@ TYPEDEF_TYPES = Union[
|
|
|
63
64
|
CONCRETE_TYPES = Union[
|
|
64
65
|
"DataType",
|
|
65
66
|
"MapType",
|
|
66
|
-
"
|
|
67
|
+
"ArrayType",
|
|
67
68
|
"NumericType",
|
|
68
69
|
"StructType",
|
|
69
70
|
"TraitDataType",
|
|
@@ -79,7 +80,6 @@ class DataType(Enum):
|
|
|
79
80
|
STRING = "string"
|
|
80
81
|
BOOL = "bool"
|
|
81
82
|
MAP = "map"
|
|
82
|
-
LIST = "list"
|
|
83
83
|
NUMBER = "number"
|
|
84
84
|
FLOAT = "float"
|
|
85
85
|
NUMERIC = "numeric"
|
|
@@ -105,7 +105,7 @@ class DataType(Enum):
|
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
class TraitDataType(BaseModel):
|
|
108
|
-
type: DataType | NumericType | StructType |
|
|
108
|
+
type: DataType | NumericType | StructType | ArrayType | MapType
|
|
109
109
|
traits: list[str]
|
|
110
110
|
|
|
111
111
|
def __hash__(self):
|
|
@@ -146,7 +146,7 @@ class NumericType(BaseModel):
|
|
|
146
146
|
return self.data_type.value
|
|
147
147
|
|
|
148
148
|
|
|
149
|
-
class
|
|
149
|
+
class ArrayType(BaseModel):
|
|
150
150
|
model_config = ConfigDict(frozen=True)
|
|
151
151
|
type: TYPEDEF_TYPES
|
|
152
152
|
|
|
@@ -159,7 +159,7 @@ class ListType(BaseModel):
|
|
|
159
159
|
|
|
160
160
|
@property
|
|
161
161
|
def data_type(self):
|
|
162
|
-
return DataType.
|
|
162
|
+
return DataType.ARRAY
|
|
163
163
|
|
|
164
164
|
@property
|
|
165
165
|
def value(self):
|
|
@@ -340,9 +340,9 @@ def dict_to_map_wrapper(arg):
|
|
|
340
340
|
|
|
341
341
|
def merge_datatypes(
|
|
342
342
|
inputs: list[
|
|
343
|
-
DataType |
|
|
343
|
+
DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
|
|
344
344
|
],
|
|
345
|
-
) -> DataType |
|
|
345
|
+
) -> DataType | ArrayType | StructType | MapType | NumericType | TraitDataType:
|
|
346
346
|
"""This is a temporary hack for doing between
|
|
347
347
|
allowable datatype transformation matrix"""
|
|
348
348
|
if len(inputs) == 1:
|
|
@@ -393,6 +393,8 @@ def arg_to_datatype(arg) -> CONCRETE_TYPES:
|
|
|
393
393
|
raise ValueError(f"Cannot parse arg datatype for arg of type {arg}")
|
|
394
394
|
elif isinstance(arg, bool):
|
|
395
395
|
return DataType.BOOL
|
|
396
|
+
elif isinstance(arg, Ordering):
|
|
397
|
+
return DataType.STRING # TODO: revisit
|
|
396
398
|
elif isinstance(arg, int):
|
|
397
399
|
return DataType.INTEGER
|
|
398
400
|
elif isinstance(arg, str):
|
|
@@ -404,14 +406,14 @@ def arg_to_datatype(arg) -> CONCRETE_TYPES:
|
|
|
404
406
|
elif isinstance(arg, TraitDataType):
|
|
405
407
|
return arg
|
|
406
408
|
elif isinstance(arg, ListWrapper):
|
|
407
|
-
return
|
|
409
|
+
return ArrayType(type=arg.type)
|
|
408
410
|
elif isinstance(arg, DataTyped):
|
|
409
411
|
return arg.output_datatype
|
|
410
412
|
elif isinstance(arg, TupleWrapper):
|
|
411
|
-
return
|
|
413
|
+
return ArrayType(type=arg.type)
|
|
412
414
|
elif isinstance(arg, list):
|
|
413
415
|
wrapper = list_to_wrapper(arg)
|
|
414
|
-
return
|
|
416
|
+
return ArrayType(type=wrapper.type)
|
|
415
417
|
elif isinstance(arg, MapWrapper):
|
|
416
418
|
return MapType(key_type=arg.key_type, value_type=arg.value_type)
|
|
417
419
|
elif isinstance(arg, datetime):
|
|
@@ -33,8 +33,8 @@ from trilogy.core.models.build import (
|
|
|
33
33
|
)
|
|
34
34
|
from trilogy.core.models.build_environment import BuildEnvironment
|
|
35
35
|
from trilogy.core.models.core import (
|
|
36
|
+
ArrayType,
|
|
36
37
|
DataType,
|
|
37
|
-
ListType,
|
|
38
38
|
ListWrapper,
|
|
39
39
|
MapType,
|
|
40
40
|
MapWrapper,
|
|
@@ -471,7 +471,7 @@ def is_scalar_condition(
|
|
|
471
471
|
| TraitDataType
|
|
472
472
|
| DataType
|
|
473
473
|
| MapWrapper[Any, Any]
|
|
474
|
-
|
|
|
474
|
+
| ArrayType
|
|
475
475
|
| MapType
|
|
476
476
|
| NumericType
|
|
477
477
|
| DatePart
|
trilogy/dialect/base.py
CHANGED
|
@@ -15,10 +15,12 @@ from trilogy.core.enums import (
|
|
|
15
15
|
ComparisonOperator,
|
|
16
16
|
DatePart,
|
|
17
17
|
FunctionType,
|
|
18
|
+
Ordering,
|
|
18
19
|
UnnestMode,
|
|
19
20
|
WindowType,
|
|
20
21
|
)
|
|
21
22
|
from trilogy.core.internal import DEFAULT_CONCEPTS
|
|
23
|
+
from trilogy.core.models.author import ArgBinding
|
|
22
24
|
from trilogy.core.models.build import (
|
|
23
25
|
BuildAggregateWrapper,
|
|
24
26
|
BuildCaseElse,
|
|
@@ -37,8 +39,8 @@ from trilogy.core.models.build import (
|
|
|
37
39
|
BuildWindowItem,
|
|
38
40
|
)
|
|
39
41
|
from trilogy.core.models.core import (
|
|
42
|
+
ArrayType,
|
|
40
43
|
DataType,
|
|
41
|
-
ListType,
|
|
42
44
|
ListWrapper,
|
|
43
45
|
MapType,
|
|
44
46
|
MapWrapper,
|
|
@@ -142,11 +144,11 @@ DATATYPE_MAP: dict[DataType, str] = {
|
|
|
142
144
|
DataType.MAP: "map",
|
|
143
145
|
DataType.DATE: "date",
|
|
144
146
|
DataType.DATETIME: "datetime",
|
|
145
|
-
DataType.
|
|
147
|
+
DataType.ARRAY: "list",
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
COMPLEX_DATATYPE_MAP = {
|
|
149
|
-
DataType.
|
|
151
|
+
DataType.ARRAY: lambda x: f"{x}[]",
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
|
|
@@ -182,6 +184,13 @@ FUNCTION_MAP = {
|
|
|
182
184
|
FunctionType.ARRAY: lambda x: f"[{', '.join(x)}]",
|
|
183
185
|
FunctionType.DATE_LITERAL: lambda x: f"date '{x}'",
|
|
184
186
|
FunctionType.DATETIME_LITERAL: lambda x: f"datetime '{x}'",
|
|
187
|
+
# ARRAY
|
|
188
|
+
FunctionType.ARRAY_SUM: lambda x: f"array_sum({x[0]})",
|
|
189
|
+
FunctionType.ARRAY_DISTINCT: lambda x: f"array_distinct({x[0]})",
|
|
190
|
+
FunctionType.ARRAY_SORT: lambda x: f"array_sort({x[0]})",
|
|
191
|
+
FunctionType.ARRAY_TRANSFORM: lambda args: (
|
|
192
|
+
f"array_transform({args[0]}, {args[1]} -> {args[2]})"
|
|
193
|
+
),
|
|
185
194
|
# math
|
|
186
195
|
FunctionType.ADD: lambda x: " + ".join(x),
|
|
187
196
|
FunctionType.ABS: lambda x: f"abs({x[0]})",
|
|
@@ -198,6 +207,7 @@ FUNCTION_MAP = {
|
|
|
198
207
|
FunctionType.COUNT_DISTINCT: lambda x: f"count(distinct {x[0]})",
|
|
199
208
|
FunctionType.COUNT: lambda x: f"count({x[0]})",
|
|
200
209
|
FunctionType.SUM: lambda x: f"sum({x[0]})",
|
|
210
|
+
FunctionType.ARRAY_AGG: lambda x: f"array_agg({x[0]})",
|
|
201
211
|
FunctionType.LENGTH: lambda x: f"length({x[0]})",
|
|
202
212
|
FunctionType.AVG: lambda x: f"avg({x[0]})",
|
|
203
213
|
FunctionType.MAX: lambda x: f"max({x[0]})",
|
|
@@ -569,7 +579,7 @@ class BaseDialect:
|
|
|
569
579
|
MapType,
|
|
570
580
|
NumericType,
|
|
571
581
|
StructType,
|
|
572
|
-
|
|
582
|
+
ArrayType,
|
|
573
583
|
ListWrapper[Any],
|
|
574
584
|
TupleWrapper[Any],
|
|
575
585
|
DatePart,
|
|
@@ -749,8 +759,12 @@ class BaseDialect:
|
|
|
749
759
|
return self.FUNCTION_MAP[FunctionType.DATETIME_LITERAL](e)
|
|
750
760
|
elif isinstance(e, TraitDataType):
|
|
751
761
|
return self.render_expr(e.type, cte=cte, cte_map=cte_map)
|
|
752
|
-
elif isinstance(e,
|
|
753
|
-
return
|
|
762
|
+
elif isinstance(e, ArgBinding):
|
|
763
|
+
return e.name
|
|
764
|
+
elif isinstance(e, Ordering):
|
|
765
|
+
return str(e.value)
|
|
766
|
+
elif isinstance(e, ArrayType):
|
|
767
|
+
return f"{self.COMPLEX_DATATYPE_MAP[DataType.ARRAY](self.render_expr(e.value_data_type, cte=cte, cte_map=cte_map))}"
|
|
754
768
|
elif isinstance(e, BuildParamaterizedConceptReference):
|
|
755
769
|
if self.rendering.parameters:
|
|
756
770
|
if e.concept.namespace == DEFAULT_NAMESPACE:
|
trilogy/dialect/duckdb.py
CHANGED
|
@@ -23,6 +23,16 @@ def generate_regex_extract(x: list[str]) -> str:
|
|
|
23
23
|
return f"REGEXP_EXTRACT({x[0]},{x[1]},{x[2]})"
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def render_sort(args):
|
|
27
|
+
if len(args) == 1:
|
|
28
|
+
return f"list_sort({args[0]})"
|
|
29
|
+
order = args[1].split(" ", 1)
|
|
30
|
+
if len(order) == 1:
|
|
31
|
+
return f"list_sort({args[0]}, '{order[0]}')"
|
|
32
|
+
elif len(order) == 2:
|
|
33
|
+
return f"list_sort({args[0]}, '{order[0]}', '{order[1]}')"
|
|
34
|
+
|
|
35
|
+
|
|
26
36
|
FUNCTION_MAP = {
|
|
27
37
|
FunctionType.COUNT: lambda args: f"count({args[0]})",
|
|
28
38
|
FunctionType.SUM: lambda args: f"sum({args[0]})",
|
|
@@ -39,6 +49,14 @@ FUNCTION_MAP = {
|
|
|
39
49
|
),
|
|
40
50
|
## Duckdb indexes from 1, not 0
|
|
41
51
|
FunctionType.INDEX_ACCESS: lambda args: (f"{args[0]}[{args[1]}]"),
|
|
52
|
+
## Duckdb uses list for array
|
|
53
|
+
FunctionType.ARRAY_DISTINCT: lambda args: f"list_distinct({args[0]})",
|
|
54
|
+
FunctionType.ARRAY_SUM: lambda args: f"list_sum({args[0]})",
|
|
55
|
+
FunctionType.ARRAY_SORT: render_sort,
|
|
56
|
+
FunctionType.ARRAY_TRANSFORM: lambda args: (
|
|
57
|
+
f"list_transform({args[0]}, {args[1]} -> {args[2]})"
|
|
58
|
+
),
|
|
59
|
+
FunctionType.ARRAY_AGG: lambda args: f"array_agg({args[0]})",
|
|
42
60
|
# datetime is aliased
|
|
43
61
|
FunctionType.CURRENT_DATETIME: lambda x: "cast(get_current_timestamp() as datetime)",
|
|
44
62
|
FunctionType.DATE: lambda x: f"cast({x[0]} as date)",
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -82,9 +82,9 @@ from trilogy.core.models.author import (
|
|
|
82
82
|
WindowItemOver,
|
|
83
83
|
)
|
|
84
84
|
from trilogy.core.models.core import (
|
|
85
|
+
ArrayType,
|
|
85
86
|
DataType,
|
|
86
87
|
DataTyped,
|
|
87
|
-
ListType,
|
|
88
88
|
ListWrapper,
|
|
89
89
|
MapType,
|
|
90
90
|
MapWrapper,
|
|
@@ -446,7 +446,7 @@ class ParseToObjects(Transformer):
|
|
|
446
446
|
@v_args(meta=True)
|
|
447
447
|
def struct_type(self, meta: Meta, args) -> StructType:
|
|
448
448
|
final: list[
|
|
449
|
-
DataType | MapType |
|
|
449
|
+
DataType | MapType | ArrayType | NumericType | StructType | Concept
|
|
450
450
|
] = []
|
|
451
451
|
for arg in args:
|
|
452
452
|
new = self.environment.concepts.__getitem__( # type: ignore
|
|
@@ -459,11 +459,11 @@ class ParseToObjects(Transformer):
|
|
|
459
459
|
fields_map={x.name: x for x in final if isinstance(x, Concept)},
|
|
460
460
|
)
|
|
461
461
|
|
|
462
|
-
def list_type(self, args) ->
|
|
462
|
+
def list_type(self, args) -> ArrayType:
|
|
463
463
|
content = args[0]
|
|
464
464
|
if isinstance(content, str):
|
|
465
465
|
content = self.environment.concepts[content]
|
|
466
|
-
return
|
|
466
|
+
return ArrayType(type=content)
|
|
467
467
|
|
|
468
468
|
def numeric_type(self, args) -> NumericType:
|
|
469
469
|
return NumericType(precision=args[0], scale=args[1])
|
|
@@ -480,13 +480,13 @@ class ParseToObjects(Transformer):
|
|
|
480
480
|
@v_args(meta=True)
|
|
481
481
|
def data_type(
|
|
482
482
|
self, meta: Meta, args
|
|
483
|
-
) -> DataType | TraitDataType |
|
|
483
|
+
) -> DataType | TraitDataType | ArrayType | StructType | MapType | NumericType:
|
|
484
484
|
resolved = args[0]
|
|
485
485
|
traits = args[2:]
|
|
486
|
-
base: DataType | TraitDataType |
|
|
486
|
+
base: DataType | TraitDataType | ArrayType | StructType | MapType | NumericType
|
|
487
487
|
if isinstance(resolved, StructType):
|
|
488
488
|
base = resolved
|
|
489
|
-
elif isinstance(resolved,
|
|
489
|
+
elif isinstance(resolved, ArrayType):
|
|
490
490
|
base = resolved
|
|
491
491
|
elif isinstance(resolved, NumericType):
|
|
492
492
|
base = resolved
|
|
@@ -524,8 +524,12 @@ class ParseToObjects(Transformer):
|
|
|
524
524
|
@v_args(meta=True)
|
|
525
525
|
def column_assignment(self, meta: Meta, args):
|
|
526
526
|
modifiers = []
|
|
527
|
-
|
|
528
|
-
|
|
527
|
+
if len(args) == 2:
|
|
528
|
+
alias = args[0]
|
|
529
|
+
concept_list = args[1]
|
|
530
|
+
else:
|
|
531
|
+
alias = args[0][-1]
|
|
532
|
+
concept_list = args[0]
|
|
529
533
|
# recursively collect modifiers
|
|
530
534
|
if len(concept_list) > 1:
|
|
531
535
|
modifiers += concept_list[:-1]
|
|
@@ -1737,6 +1741,10 @@ class ParseToObjects(Transformer):
|
|
|
1737
1741
|
def sum(self, meta, args):
|
|
1738
1742
|
return self.function_factory.create_function(args, FunctionType.SUM, meta)
|
|
1739
1743
|
|
|
1744
|
+
@v_args(meta=True)
|
|
1745
|
+
def array_agg(self, meta, args):
|
|
1746
|
+
return self.function_factory.create_function(args, FunctionType.ARRAY_AGG, meta)
|
|
1747
|
+
|
|
1740
1748
|
@v_args(meta=True)
|
|
1741
1749
|
def avg(self, meta, args):
|
|
1742
1750
|
return self.function_factory.create_function(args, FunctionType.AVG, meta)
|
|
@@ -2002,6 +2010,51 @@ class ParseToObjects(Transformer):
|
|
|
2002
2010
|
def fbool(self, meta, args):
|
|
2003
2011
|
return self.function_factory.create_function(args, FunctionType.BOOL, meta)
|
|
2004
2012
|
|
|
2013
|
+
@v_args(meta=True)
|
|
2014
|
+
def farray_sum(self, meta, args):
|
|
2015
|
+
return self.function_factory.create_function(args, FunctionType.ARRAY_SUM, meta)
|
|
2016
|
+
|
|
2017
|
+
@v_args(meta=True)
|
|
2018
|
+
def farray_distinct(self, meta, args):
|
|
2019
|
+
return self.function_factory.create_function(
|
|
2020
|
+
args, FunctionType.ARRAY_DISTINCT, meta
|
|
2021
|
+
)
|
|
2022
|
+
|
|
2023
|
+
@v_args(meta=True)
|
|
2024
|
+
def farray_sort(self, meta, args):
|
|
2025
|
+
if len(args) == 1:
|
|
2026
|
+
# this is a magic value to represent the default behavior
|
|
2027
|
+
args = [args[0], Ordering.ASCENDING]
|
|
2028
|
+
return self.function_factory.create_function(
|
|
2029
|
+
args, FunctionType.ARRAY_SORT, meta
|
|
2030
|
+
)
|
|
2031
|
+
|
|
2032
|
+
@v_args(meta=True)
|
|
2033
|
+
def transform_lambda(self, meta, args):
|
|
2034
|
+
return self.environment.functions[args[0]]
|
|
2035
|
+
|
|
2036
|
+
@v_args(meta=True)
|
|
2037
|
+
def farray_transform(self, meta, args):
|
|
2038
|
+
factory: CustomFunctionFactory = args[1]
|
|
2039
|
+
if not len(factory.function_arguments) == 1:
|
|
2040
|
+
raise InvalidSyntaxException(
|
|
2041
|
+
"Array transform function must have exactly one argument;"
|
|
2042
|
+
)
|
|
2043
|
+
return self.function_factory.create_function(
|
|
2044
|
+
[
|
|
2045
|
+
args[0],
|
|
2046
|
+
factory.function_arguments[0],
|
|
2047
|
+
factory(
|
|
2048
|
+
ArgBinding(
|
|
2049
|
+
name=factory.function_arguments[0].name,
|
|
2050
|
+
datatype=arg_to_datatype(args[0]).value_data_type,
|
|
2051
|
+
)
|
|
2052
|
+
),
|
|
2053
|
+
],
|
|
2054
|
+
FunctionType.ARRAY_TRANSFORM,
|
|
2055
|
+
meta,
|
|
2056
|
+
)
|
|
2057
|
+
|
|
2005
2058
|
|
|
2006
2059
|
def unpack_visit_error(e: VisitError, text: str | None = None):
|
|
2007
2060
|
"""This is required to get exceptions from imports, which would
|
trilogy/parsing/render.py
CHANGED
|
@@ -28,8 +28,8 @@ from trilogy.core.models.author import (
|
|
|
28
28
|
WindowItem,
|
|
29
29
|
)
|
|
30
30
|
from trilogy.core.models.core import (
|
|
31
|
+
ArrayType,
|
|
31
32
|
DataType,
|
|
32
|
-
ListType,
|
|
33
33
|
ListWrapper,
|
|
34
34
|
MapWrapper,
|
|
35
35
|
NumericType,
|
|
@@ -332,7 +332,7 @@ class Renderer:
|
|
|
332
332
|
return output
|
|
333
333
|
|
|
334
334
|
@to_string.register
|
|
335
|
-
def _(self, arg:
|
|
335
|
+
def _(self, arg: ArrayType):
|
|
336
336
|
return f"list<{self.to_string(arg.value_data_type)}>"
|
|
337
337
|
|
|
338
338
|
@to_string.register
|
trilogy/parsing/trilogy.lark
CHANGED
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
|
|
52
52
|
//column_assignment
|
|
53
53
|
//figure out if we want static
|
|
54
|
-
column_assignment: (raw_column_assignment | IDENTIFIER | QUOTED_IDENTIFIER | expr ) ":" concept_assignment
|
|
54
|
+
column_assignment: ((raw_column_assignment | IDENTIFIER | QUOTED_IDENTIFIER | expr ) ":" concept_assignment) | concept_assignment
|
|
55
55
|
|
|
56
56
|
RAW_ENTRY.1: /raw\s*\(/s
|
|
57
57
|
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
|
|
163
163
|
between_comparison: "between"i expr "and"i expr
|
|
164
164
|
|
|
165
|
-
subselect_comparison: expr array_comparison (literal | _constant_functions | _string_functions | concept_lit | filter_item | window_item | unnest | fgroup | expr_tuple | parenthetical )
|
|
165
|
+
subselect_comparison: expr array_comparison (literal | _constant_functions | _string_functions | _array_functions | concept_lit | filter_item | window_item | unnest | fgroup | expr_tuple | parenthetical )
|
|
166
166
|
|
|
167
167
|
expr_tuple: ("(" expr ("," expr)+ ","? ")") | ("(" expr "," ")")
|
|
168
168
|
|
|
@@ -286,6 +286,19 @@
|
|
|
286
286
|
fregexp_replace: _REGEXP_REPLACE expr "," expr "," expr ")"
|
|
287
287
|
|
|
288
288
|
_string_functions: like | ilike | upper | flower | fsplit | fstrpos | fsubstring | fcontains | fregexp_extract | fregexp_contains | fregexp_replace
|
|
289
|
+
|
|
290
|
+
//array_functions
|
|
291
|
+
_ARRAY_SUM.1: "array_sum("i
|
|
292
|
+
farray_sum: _ARRAY_SUM expr ")"
|
|
293
|
+
_ARRAY_DISTINCT.1: "array_distinct("i
|
|
294
|
+
farray_distinct: _ARRAY_DISTINCT expr ")"
|
|
295
|
+
_ARRAY_SORT.1: "array_sort("i
|
|
296
|
+
farray_sort: _ARRAY_SORT expr ("," ordering )? ")"
|
|
297
|
+
_ARRAY_TRANSFORM.1: "array_transform("i
|
|
298
|
+
transform_lambda: "@" IDENTIFIER
|
|
299
|
+
farray_transform: _ARRAY_TRANSFORM expr "," transform_lambda ")"
|
|
300
|
+
|
|
301
|
+
_array_functions: farray_sum | farray_distinct | farray_sort | farray_transform
|
|
289
302
|
|
|
290
303
|
// special aggregate
|
|
291
304
|
_GROUP.1: "group("i
|
|
@@ -309,11 +322,13 @@
|
|
|
309
322
|
max: _MAX expr ")"
|
|
310
323
|
_MIN.1: "min("i
|
|
311
324
|
min: _MIN expr ")"
|
|
325
|
+
_ARRAY_AGG.1: "array_agg("i
|
|
326
|
+
array_agg: _ARRAY_AGG expr ")"
|
|
312
327
|
|
|
313
328
|
//aggregates can force a grain
|
|
314
329
|
aggregate_all: "*"
|
|
315
330
|
aggregate_over: ("BY"i (aggregate_all | over_list))
|
|
316
|
-
aggregate_functions: (count | count_distinct | sum | avg | max | min) aggregate_over?
|
|
331
|
+
aggregate_functions: (count | count_distinct | sum | avg | max | min | array_agg) aggregate_over?
|
|
317
332
|
|
|
318
333
|
// date functions
|
|
319
334
|
_DATE.1: "date("i
|
|
@@ -355,7 +370,7 @@
|
|
|
355
370
|
|
|
356
371
|
_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
|
|
357
372
|
|
|
358
|
-
_static_functions: _string_functions | _math_functions
|
|
373
|
+
_static_functions: _string_functions | _math_functions | _array_functions
|
|
359
374
|
|
|
360
375
|
custom_function: "@" IDENTIFIER "(" (expr ",")* expr ")"
|
|
361
376
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|