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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.73
3
+ Version: 0.0.3.75
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,5 +1,5 @@
1
- pytrilogy-0.0.3.73.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
- trilogy/__init__.py,sha256=QMg3__Goia6PP_-WfMP9ZmROmpZ0XzX4rCz9MvpBb_w,303
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=h-Ag7vT76tsjib9BfjOgI-yVpuJDgpn2TSps-ibRAj8,2593
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=fog9j6YU22LXuoArKsNJn-UFIEAVk06LI_-Zdg2UDv0,7841
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=xT4DR6IRTt3N6TZ_LEcSV2nvLUWFGfajd5vQZd2Pw7k,29512
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=Wz_6yEZS5EyXJ5wHHl-C44ikFka5XftBYOjNRK98Hfw,77790
27
- trilogy/core/models/build.py,sha256=59KmJDY1lzJMXuAZjxvT7K5IhIga9k2xwhzQBbx5zyo,65688
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=EMAuWTngoNVGCdfNrAY7_k6g528iodNQLwPRVip-8DA,10980
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=ChD1lP2iXtfsI1bS08OZsJ2o9t877OhNXrDvyEgYdmY,22581
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=5FI4_XDdSavdHeC3VhSI8xrvQagFyRRq6Ttsh70ajo4,45241
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=gsXhPKX0D7ykJ9RFK9qx8uBTjLgtHu6PYv6GlBFtnJE,4448
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=YFIWTkWrUJ8l-KoCVwPs1hodj3BhqX-DHbBXWQN5akc,72776
101
- trilogy/parsing/render.py,sha256=gGCFj2ue0UoaU2MR6qHGMAHXkYRMkTmHjnBowdcgFMY,19603
102
- trilogy/parsing/trilogy.lark,sha256=1RIqA7zrGuqDJYSv9yHGSw0vdIfGOLPOnc4hSBRSTVU,14346
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.73.dist-info/METADATA,sha256=hMwyLjCer0xl2zhn1XH4PijoJrNy7oXV9qKyHIDfUWA,9734
114
- pytrilogy-0.0.3.73.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
115
- pytrilogy-0.0.3.73.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
116
- pytrilogy-0.0.3.73.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
117
- pytrilogy-0.0.3.73.dist-info/RECORD,,
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
@@ -4,6 +4,6 @@ from trilogy.dialect.enums import Dialects
4
4
  from trilogy.executor import Executor
5
5
  from trilogy.parser import parse
6
6
 
7
- __version__ = "0.0.3.73"
7
+ __version__ = "0.0.3.75"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -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
- "ListType",
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 | ListType | MapType | StructType | NumericType | TraitDataType | None
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, (ListType, MapType)):
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, ListType):
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=ListType(type=DataType.STRING),
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=ListType(type=DataType.STRING),
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 | ListType | StructType | MapType | NumericType | TraitDataType
887
+ DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
841
888
  ] = None,
842
889
  output_purpose: Optional[Purpose] = None,
843
890
  ) -> Concept:
@@ -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 | ListType | StructType | MapType | NumericType
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, ListType) and not is_compatible_datatype(
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 | ListType | StructType | MapType | NumericType
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 | ListType | StructType | MapType | NumericType
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 | ListType | StructType | MapType | NumericType | TraitDataType,
1440
+ type: DataType | ArrayType | StructType | MapType | NumericType | TraitDataType,
1441
1441
  ) -> DataType:
1442
- if isinstance(type, ListType):
1443
- return DataType.LIST
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 | ListType | StructType | MapType | NumericType | TraitDataType
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
- | ListType
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
  )
@@ -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 | ListType | StructType | MapType | NumericType
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 | ListType | StructType | MapType | NumericType | TraitDataType
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
- ListType,
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: ListType):
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] = {}
@@ -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
- "ListType",
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
- "ListType",
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 | ListType | MapType
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 ListType(BaseModel):
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.LIST
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 | ListType | StructType | MapType | NumericType | TraitDataType
343
+ DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
344
344
  ],
345
- ) -> DataType | ListType | StructType | MapType | NumericType | TraitDataType:
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 ListType(type=arg.type)
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 ListType(type=arg.type)
413
+ return ArrayType(type=arg.type)
412
414
  elif isinstance(arg, list):
413
415
  wrapper = list_to_wrapper(arg)
414
- return ListType(type=wrapper.type)
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
- | ListType
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.LIST: "list",
147
+ DataType.ARRAY: "list",
146
148
  }
147
149
 
148
150
  COMPLEX_DATATYPE_MAP = {
149
- DataType.LIST: lambda x: f"{x}[]",
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
- ListType,
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, ListType):
753
- return f"{self.COMPLEX_DATATYPE_MAP[DataType.LIST](self.render_expr(e.value_data_type, cte=cte, cte_map=cte_map))}"
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)",
@@ -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 | ListType | NumericType | StructType | Concept
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) -> ListType:
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 ListType(type=content)
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 | ListType | StructType | MapType | NumericType:
483
+ ) -> DataType | TraitDataType | ArrayType | StructType | MapType | NumericType:
484
484
  resolved = args[0]
485
485
  traits = args[2:]
486
- base: DataType | TraitDataType | ListType | StructType | MapType | NumericType
486
+ base: DataType | TraitDataType | ArrayType | StructType | MapType | NumericType
487
487
  if isinstance(resolved, StructType):
488
488
  base = resolved
489
- elif isinstance(resolved, ListType):
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
- alias = args[0]
528
- concept_list = args[1]
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: ListType):
335
+ def _(self, arg: ArrayType):
336
336
  return f"list<{self.to_string(arg.value_data_type)}>"
337
337
 
338
338
  @to_string.register
@@ -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