pytrilogy 0.0.3.60__py3-none-any.whl → 0.0.3.61__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.60
3
+ Version: 0.0.3.61
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.60.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
- trilogy/__init__.py,sha256=8jDnsd42NfWTeVpYr5aVIC5F7YtNxUA3qJ-UpkKPzbU,303
1
+ pytrilogy-0.0.3.61.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
+ trilogy/__init__.py,sha256=6pE1HEb6lBSW2ODExpQU2E4nDuvhlPqat8goqgVFph4,303
3
3
  trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  trilogy/constants.py,sha256=lv_aJWP6dn6e2aF4BAE72jbnNtceFddfqtiDSsvzno0,1692
5
5
  trilogy/engine.py,sha256=OK2RuqCIUId6yZ5hfF8J1nxGP0AJqHRZiafcowmW0xc,1728
@@ -11,7 +11,7 @@ trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
11
11
  trilogy/authoring/__init__.py,sha256=h-Ag7vT76tsjib9BfjOgI-yVpuJDgpn2TSps-ibRAj8,2593
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=z2mljNfhosnap4Nesx-mVOzM4UAPYU0R-Gy-HQVqcBI,7781
14
+ trilogy/core/enums.py,sha256=2sgoVzHQpscIx_WUqJMH0oGxtcg41gN0l1qkRjzK2iI,7803
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
@@ -19,16 +19,16 @@ trilogy/core/exceptions.py,sha256=JPYyBcit3T_pRtlHdtKSeVJkIyWUTozW2aaut25A2xI,67
19
19
  trilogy/core/functions.py,sha256=poVfAwet1xdxTkC7WL38UmGRDpUVO9iSMNWSagl9_r4,29302
20
20
  trilogy/core/graph_models.py,sha256=z17EoO8oky2QOuO6E2aMWoVNKEVJFhLdsQZOhC4fNLU,2079
21
21
  trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
22
- trilogy/core/optimization.py,sha256=lE5WSTsDHOaqvh-G9bOfbegP3R7NDqD4jrD3VAYxEHY,8853
22
+ trilogy/core/optimization.py,sha256=ojpn-p79lr03SSVQbbw74iPCyoYpDYBmj1dbZ3oXCjI,8860
23
23
  trilogy/core/query_processor.py,sha256=QiE_w5HgheT4GLZFnaLssJ4plf4voK0TeTd6N3jhR6A,20188
24
24
  trilogy/core/utility.py,sha256=3VC13uSQWcZNghgt7Ot0ZTeEmNqs__cx122abVq9qhM,410
25
25
  trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  trilogy/core/models/author.py,sha256=8XbIsQr6cQrgo9uzee5qRoYiMdEG7yKF4FiiWImW7U0,77490
27
- trilogy/core/models/build.py,sha256=yBiOQ4Bhjz09pSD1jSGhhf9QFFQuplrvZ0JQB5-iXHk,63104
27
+ trilogy/core/models/build.py,sha256=VAJpmk3GQCeJHpgjbFYHDXnFZlSD89sb0RvtIsZ58JE,63129
28
28
  trilogy/core/models/build_environment.py,sha256=s_C9xAHuD3yZ26T15pWVBvoqvlp2LdZ8yjsv2_HdXLk,5363
29
- trilogy/core/models/core.py,sha256=wx6hJcFECMG-Ij972ADNkr-3nFXkYESr82ObPiC46_U,10875
29
+ trilogy/core/models/core.py,sha256=EMAuWTngoNVGCdfNrAY7_k6g528iodNQLwPRVip-8DA,10980
30
30
  trilogy/core/models/datasource.py,sha256=6RjJUd2u4nYmEwFBpJlM9LbHVYDv8iHJxqiBMZqUrwI,9422
31
- trilogy/core/models/environment.py,sha256=AVSrvjNcNX535GhCPtYhCRY2Lp_Hj0tdY3VVt_kZb9Q,27260
31
+ trilogy/core/models/environment.py,sha256=tM8SwH9r1ZSc-F0Enod7r1NuqzGpQfLcPxnrtK5Hqpk,27398
32
32
  trilogy/core/models/execute.py,sha256=hOilC-lka4W-C8Pakb0Vd1-T0oskeWdC8Ls0bm8_388,43109
33
33
  trilogy/core/optimizations/__init__.py,sha256=YH2-mGXZnVDnBcWVi8vTbrdw7Qs5TivG4h38rH3js_I,290
34
34
  trilogy/core/optimizations/base_optimization.py,sha256=gzDOKImoFn36k7XBD3ysEYDnbnb6vdVIztUfFQZsGnM,513
@@ -43,7 +43,7 @@ trilogy/core/processing/discovery_validation.py,sha256=Ek9jviFgimLMUMYLXBChUQmOD
43
43
  trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuXl-Te85lag,1205
44
44
  trilogy/core/processing/utility.py,sha256=mrfR9pgek-xjxoDQSlvPqOW9dpmREjgzqn4AGoqpGeM,22774
45
45
  trilogy/core/processing/node_generators/__init__.py,sha256=w8TQQgNhyAra6JQHdg1_Ags4BGyxjXYruu6UeC5yOkI,873
46
- trilogy/core/processing/node_generators/basic_node.py,sha256=kyOAiVaYnUlWWRs4y6ctQPlisQ2kCRZ1wZYmJfbkRSw,4337
46
+ trilogy/core/processing/node_generators/basic_node.py,sha256=8NtHZ99mJhJwDIib3xlcrn7oWmE42HHKuBpnwXIzpyo,4861
47
47
  trilogy/core/processing/node_generators/common.py,sha256=PdysdroW9DUADP7f5Wv_GKPUyCTROZV1g3L45fawxi8,9443
48
48
  trilogy/core/processing/node_generators/filter_node.py,sha256=0hdfiS2I-Jvr6P-il3jnAJK-g-DMG7_cFbZGCnLnJAo,10032
49
49
  trilogy/core/processing/node_generators/group_node.py,sha256=nIfiMrJQEksUfqAeeA3X5PS1343y4lmPTipYuCa-rvs,6141
@@ -54,9 +54,9 @@ trilogy/core/processing/node_generators/recursive_node.py,sha256=l5zdh0dURKwmAy8
54
54
  trilogy/core/processing/node_generators/rowset_node.py,sha256=2BiSsegbRF9csJ_Xl8P_CxIm4dAAb7dF29u6v_Odr-A,6709
55
55
  trilogy/core/processing/node_generators/select_merge_node.py,sha256=lxXhMhDKGbu67QFNbbAT-BO8gbWppIvjn_hAXpLEPe0,19953
56
56
  trilogy/core/processing/node_generators/select_node.py,sha256=3dvw0d53eUtCRCUPN6J48I3qBEX1Wha7saQ_ndPu6_I,1777
57
- trilogy/core/processing/node_generators/synonym_node.py,sha256=a_RllD_5b4wg4JtiEkxJOfroFdEXJq6P4VUjga7sv5w,2300
57
+ trilogy/core/processing/node_generators/synonym_node.py,sha256=BNfdwTunCerIzm7YLISI_XcfxxUzWLrZrRvZvayScSk,2257
58
58
  trilogy/core/processing/node_generators/union_node.py,sha256=VNo6Oey4p8etU9xrOh2oTT2lIOTvY6PULUPRvVa2uxU,2877
59
- trilogy/core/processing/node_generators/unnest_node.py,sha256=uWug51k3WtvFzj1uKyEoori0nDfvxeaiLbvf7ZmfYCM,2666
59
+ trilogy/core/processing/node_generators/unnest_node.py,sha256=ueOQtoTf2iJHO09RzWHDFQ5iKZq2fVhGf2KAF2U2kU8,2677
60
60
  trilogy/core/processing/node_generators/window_node.py,sha256=GP3Hvkbb0TDA6ef7W7bmvQEHVH-NRIfBT_0W4fcH3g4,6529
61
61
  trilogy/core/processing/node_generators/select_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
62
  trilogy/core/processing/node_generators/select_helpers/datasource_injection.py,sha256=GMW07bb6hXurhF0hZLYoMAKSIS65tat5hwBjvqqPeSA,6516
@@ -76,15 +76,15 @@ 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=V9003GLDMf1fg-EhphlxKov84f1LKqhyYfnqujXblPg,42754
79
+ trilogy/dialect/base.py,sha256=CVOkIm3pwcuWlPGRqiimXjoKfns-2dAYlGsMCHGuzY4,43036
80
80
  trilogy/dialect/bigquery.py,sha256=6ghCqy-k7UioIJc1EEQ7gRo_PHaO8Vm7yYbiQ-kgpzs,3629
81
- trilogy/dialect/common.py,sha256=61yWE0K6M-Hfc934HkdHakIdLAUNB5kPSAQcTeAo3sU,5917
81
+ trilogy/dialect/common.py,sha256=hhzuMTFW9QQIP7TKLT9BlJy6lw2R03a68jKQ-7t4-2c,6070
82
82
  trilogy/dialect/config.py,sha256=olnyeVU5W5T6b9-dMeNAnvxuPlyc2uefb7FRME094Ec,3834
83
83
  trilogy/dialect/dataframe.py,sha256=RUbNgReEa9g3pL6H7fP9lPTrAij5pkqedpZ99D8_5AE,1522
84
84
  trilogy/dialect/duckdb.py,sha256=C5TovwacDXo9YDpMTpPxkH7D0AxQERa7JL1RUkDGVng,3898
85
85
  trilogy/dialect/enums.py,sha256=FRNYQ5-w-B6-X0yXKNU5g9GowsMlERFogTC5u2nxL_s,4740
86
86
  trilogy/dialect/postgres.py,sha256=VH4EB4myjIeZTHeFU6vK00GxY9c53rCBjg2mLbdaCEE,3254
87
- trilogy/dialect/presto.py,sha256=Mw7_F8h19mWfuZHkHQJizQWbpu1lIHe6t2PA0r88gsY,3392
87
+ trilogy/dialect/presto.py,sha256=Wd0yHq3EOSfCOy7lWPfCr13JHO3olsm8qUXgml-oTm0,3529
88
88
  trilogy/dialect/snowflake.py,sha256=LQIcHuyuGZXbxrv6sH17aLXLzw7yFVuRoE9M4doNk5k,3187
89
89
  trilogy/dialect/sql_server.py,sha256=z2Vg7Qvw83rbGiEFIvHHLqVWJTWiz2xs76kpQj4HdTU,3131
90
90
  trilogy/hooks/__init__.py,sha256=T3SF3phuUDPLXKGRVE_Lf9mzuwoXWyaLolncR_1kY30,144
@@ -97,9 +97,9 @@ trilogy/parsing/common.py,sha256=OOAssbo6Yp3L4u2OSGUYIuC_iAG_843vLpUtLkk1ub8,299
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=ioKWF6EFj1AIyg_pqPB9L6AjalkKQYoNav4cw4orWik,71368
101
- trilogy/parsing/render.py,sha256=hI4y-xjXrEXvHslY2l2TQ8ic0zAOpN41ADH37J2_FZY,19047
102
- trilogy/parsing/trilogy.lark,sha256=CK7Hqqr9W6FYb1BRWd9k3B6SzhJ8GgK4lu4hALxn_kw,14307
100
+ trilogy/parsing/parse_engine.py,sha256=O7aM5nZ4SjKlqO2x8XWefI1BMCW06jYYLhABU4k1HCI,72430
101
+ trilogy/parsing/render.py,sha256=xUhG0gw4atYFO79kKL_U5UkHMma-oorN5vjPJ5zTBdU,19383
102
+ trilogy/parsing/trilogy.lark,sha256=x9D1BXtE1E9Kxatx5Kt7xCaid8zgedabwca_B7j7L7o,14331
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=-bMV6dyofskl4Kvows-iQ4JCxjVUwsZOeWCy8JO5Ftw,135
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.60.dist-info/METADATA,sha256=n3i-LaY9KCLDvVzwfo7Z1xuUqYRPZR3LMXmOGFwBeGU,9095
114
- pytrilogy-0.0.3.60.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
115
- pytrilogy-0.0.3.60.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
116
- pytrilogy-0.0.3.60.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
117
- pytrilogy-0.0.3.60.dist-info/RECORD,,
113
+ pytrilogy-0.0.3.61.dist-info/METADATA,sha256=rXq2NHO2bCO_JhPw5mu8Xj2lEOHcnX73fENl0m7-P2s,9095
114
+ pytrilogy-0.0.3.61.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
115
+ pytrilogy-0.0.3.61.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
116
+ pytrilogy-0.0.3.61.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
117
+ pytrilogy-0.0.3.61.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.60"
7
+ __version__ = "0.0.3.61"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
trilogy/core/enums.py CHANGED
@@ -9,6 +9,7 @@ class UnnestMode(Enum):
9
9
  CROSS_JOIN = "cross_join"
10
10
  CROSS_JOIN_UNNEST = "cross_join_unnest"
11
11
  CROSS_JOIN_ALIAS = "cross_join_alias"
12
+ PRESTO = "presto"
12
13
  SNOWFLAKE = "snowflake"
13
14
 
14
15
 
@@ -131,7 +131,7 @@ def concept_is_relevant(
131
131
  if (
132
132
  concept.purpose == Purpose.KEY
133
133
  and concept.keys
134
- and all([c in others for c in concept.keys])
134
+ and all([c in others and c != concept.address for c in concept.keys])
135
135
  ):
136
136
  return False
137
137
  if concept.purpose in (Purpose.METRIC,):
@@ -175,13 +175,17 @@ class ListType(BaseModel):
175
175
 
176
176
 
177
177
  class MapType(BaseModel):
178
- key_type: DataType
178
+ key_type: TYPEDEF_TYPES
179
179
  value_type: TYPEDEF_TYPES
180
180
 
181
181
  @field_validator("value_type", mode="plain")
182
182
  def validate_type(cls, v):
183
183
  return v
184
184
 
185
+ @field_validator("key_type", mode="plain")
186
+ def validate_key_type(cls, v):
187
+ return v
188
+
185
189
  @property
186
190
  def data_type(self):
187
191
  return DataType.MAP
@@ -58,7 +58,9 @@ if TYPE_CHECKING:
58
58
  class Import:
59
59
  alias: str
60
60
  path: Path
61
- input_path: str | None = None
61
+ input_path: Path | None = (
62
+ None # filepath where the text came from (path is the import path, but may be resolved from a dictionary for some resolvers)
63
+ )
62
64
 
63
65
 
64
66
  class BaseImportResolver(BaseModel):
@@ -52,7 +52,7 @@ def reorder_ctes(
52
52
  return input
53
53
  return [mapping[x] for x in topological_order]
54
54
  except nx.NetworkXUnfeasible as e:
55
- print(
55
+ logger.error(
56
56
  "The graph is not a DAG (contains cycles) and cannot be topologically sorted."
57
57
  )
58
58
  raise e
@@ -21,6 +21,8 @@ def is_equivalent_basic_function_lineage(
21
21
  y.lineage, BuildFunction
22
22
  ):
23
23
  return False
24
+ if x.lineage.operator == y.lineage.operator == FunctionType.ATTR_ACCESS:
25
+ return x.lineage.concept_arguments == y.lineage.concept_arguments
24
26
  if x.lineage.operator == y.lineage.operator:
25
27
  return True
26
28
  if (
@@ -55,9 +57,14 @@ def gen_basic_node(
55
57
  f"{depth_prefix}{LOGGER_PREFIX} checking for synonyms for attribute access"
56
58
  )
57
59
  for x in local_optional:
60
+ found = False
58
61
  for z in x.pseudonyms:
62
+ # gate to ensure we don't match to multiple synonyms
63
+ if found:
64
+ continue
59
65
  s_concept = environment.alias_origin_lookup[z]
60
66
  if is_equivalent_basic_function_lineage(concept, s_concept):
67
+ found = True
61
68
  synonyms.append(s_concept)
62
69
  ignored_optional.add(x.address)
63
70
  equivalent_optional = [
@@ -72,7 +79,11 @@ def gen_basic_node(
72
79
  f"{depth_prefix}{LOGGER_PREFIX} basic node for {concept} has equivalent optional {[x.address for x in equivalent_optional]}"
73
80
  )
74
81
  for eo in equivalent_optional:
75
- parent_concepts += resolve_function_parent_concepts(eo, environment=environment)
82
+ new_parents = resolve_function_parent_concepts(eo, environment=environment)
83
+ logger.info(
84
+ f"{depth_prefix}{LOGGER_PREFIX} equivalent optional {eo.address} has parents {[x.address for x in new_parents]}"
85
+ )
86
+ parent_concepts += new_parents
76
87
  non_equivalent_optional = [
77
88
  x
78
89
  for x in local_optional
@@ -64,6 +64,5 @@ def gen_synonym_node(
64
64
  )
65
65
  if attempt:
66
66
  logger.info(f"{local_prefix} found inputs with {combo}")
67
- print(attempt.output_concepts)
68
67
  return attempt
69
68
  return None
@@ -31,7 +31,7 @@ def gen_unnest_node(
31
31
  ]
32
32
  all_parents = arguments + non_equivalent_optional
33
33
  logger.info(
34
- f"{depth_prefix}{LOGGER_PREFIX} unnest node for {concept} with lineage {concept.lineage} has parents {all_parents} and equivalent optional {equivalent_optional}"
34
+ f"{depth_prefix}{LOGGER_PREFIX} unnest node for {concept} with lineage {concept.lineage} has parents + optional {all_parents} and equivalent optional {equivalent_optional}"
35
35
  )
36
36
  if arguments or local_optional:
37
37
  parent = source_concepts(
trilogy/dialect/base.py CHANGED
@@ -757,7 +757,7 @@ class BaseDialect:
757
757
  f"{self.QUOTE_CHARACTER}{c.safe_address}{self.QUOTE_CHARACTER}"
758
758
  for c in cte.join_derived_concepts
759
759
  ]
760
- elif self.UNNEST_MODE == UnnestMode.CROSS_JOIN_UNNEST:
760
+ elif self.UNNEST_MODE in (UnnestMode.CROSS_JOIN_UNNEST, UnnestMode.PRESTO):
761
761
  select_columns = [
762
762
  self.render_concept_sql(c, cte)
763
763
  for c in cte.output_columns
@@ -787,12 +787,14 @@ class BaseDialect:
787
787
  ):
788
788
 
789
789
  source = f"{render_unnest(self.UNNEST_MODE, self.QUOTE_CHARACTER, cte.join_derived_concepts[0], self.render_expr, cte)}"
790
- elif (
791
- cte.join_derived_concepts
792
- and self.UNNEST_MODE == UnnestMode.CROSS_JOIN_UNNEST
790
+ elif cte.join_derived_concepts and self.UNNEST_MODE in (
791
+ UnnestMode.CROSS_JOIN_UNNEST,
793
792
  ):
794
793
  source = f"{self.render_expr(cte.join_derived_concepts[0], cte)} as {self.QUOTE_CHARACTER}{UNNEST_NAME}{self.QUOTE_CHARACTER}"
795
-
794
+ elif cte.join_derived_concepts and self.UNNEST_MODE in (
795
+ UnnestMode.PRESTO,
796
+ ):
797
+ source = f"{self.render_expr(cte.join_derived_concepts[0], cte)} as t({self.QUOTE_CHARACTER}{UNNEST_NAME}{self.QUOTE_CHARACTER})"
796
798
  elif (
797
799
  cte.join_derived_concepts
798
800
  and self.UNNEST_MODE == UnnestMode.SNOWFLAKE
trilogy/dialect/common.py CHANGED
@@ -35,7 +35,6 @@ def render_unnest(
35
35
  cte: CTE,
36
36
  ):
37
37
  if not isinstance(concept, (BuildConcept, BuildParamaterizedConceptReference)):
38
- print(type(concept))
39
38
  address = UNNEST_NAME
40
39
  else:
41
40
  address = concept.safe_address
@@ -43,6 +42,8 @@ def render_unnest(
43
42
  return f"{render_func(concept, cte)} as {quote_character}{address}{quote_character}"
44
43
  elif unnest_mode == UnnestMode.CROSS_JOIN_UNNEST:
45
44
  return f"unnest({render_func(concept, cte)}) as {quote_character}{address}{quote_character}"
45
+ elif unnest_mode == UnnestMode.PRESTO:
46
+ return f"unnest({render_func(concept, cte)}) as t({quote_character}{UNNEST_NAME}{quote_character})"
46
47
  elif unnest_mode == UnnestMode.CROSS_JOIN_ALIAS:
47
48
  return f"{render_func(concept, cte)} as unnest_wrapper ({quote_character}{address}{quote_character})"
48
49
  elif unnest_mode == UnnestMode.SNOWFLAKE:
@@ -103,6 +104,7 @@ def render_join(
103
104
  UnnestMode.CROSS_JOIN,
104
105
  UnnestMode.CROSS_JOIN_UNNEST,
105
106
  UnnestMode.CROSS_JOIN_ALIAS,
107
+ UnnestMode.PRESTO,
106
108
  ):
107
109
  return f"CROSS JOIN {render_unnest(unnest_mode, quote_character, join.object_to_unnest, render_expr_func, cte)}"
108
110
  if unnest_mode == UnnestMode.SNOWFLAKE:
trilogy/dialect/presto.py CHANGED
@@ -33,6 +33,8 @@ FUNCTION_MAP = {
33
33
  FunctionType.CURRENT_DATE: lambda x: "CURRENT_DATE",
34
34
  FunctionType.CURRENT_DATETIME: lambda x: "CURRENT_TIMESTAMP",
35
35
  FunctionType.ARRAY: lambda x: f"ARRAY[{', '.join(x)}]",
36
+ # regex
37
+ FunctionType.REGEXP_CONTAINS: lambda x: f"REGEXP_LIKE({x[0]}, {x[1]})",
36
38
  }
37
39
 
38
40
  FUNCTION_GRAIN_MATCH_MAP = {
@@ -85,8 +87,12 @@ class PrestoDialect(BaseDialect):
85
87
  }
86
88
  QUOTE_CHARACTER = '"'
87
89
  SQL_TEMPLATE = SQL_TEMPLATE
88
- DATATYPE_MAP = {**BaseDialect.DATATYPE_MAP, DataType.NUMERIC: "DECIMAL"}
89
- UNNEST_MODE = UnnestMode.CROSS_JOIN_ALIAS
90
+ DATATYPE_MAP = {
91
+ **BaseDialect.DATATYPE_MAP,
92
+ DataType.NUMERIC: "DECIMAL",
93
+ DataType.STRING: "VARCHAR",
94
+ }
95
+ UNNEST_MODE = UnnestMode.PRESTO
90
96
 
91
97
 
92
98
  class TrinoDialect(PrestoDialect):
@@ -460,13 +460,22 @@ class ParseToObjects(Transformer):
460
460
  )
461
461
 
462
462
  def list_type(self, args) -> ListType:
463
- return ListType(type=args[0])
463
+ content = args[0]
464
+ if isinstance(content, str):
465
+ content = self.environment.concepts[content]
466
+ return ListType(type=content)
464
467
 
465
468
  def numeric_type(self, args) -> NumericType:
466
469
  return NumericType(precision=args[0], scale=args[1])
467
470
 
468
471
  def map_type(self, args) -> MapType:
469
- return MapType(key_type=args[0], value_type=args[1])
472
+ key = args[0]
473
+ value = args[1]
474
+ if isinstance(key, str):
475
+ key = self.environment.concepts[key]
476
+ elif isinstance(value, str):
477
+ value = self.environment.concepts[value]
478
+ return MapType(key_type=key, value_type=value)
470
479
 
471
480
  @v_args(meta=True)
472
481
  def data_type(
@@ -842,6 +851,17 @@ class ParseToObjects(Transformer):
842
851
  continue
843
852
 
844
853
  key_inputs = grain.components
854
+ eligible = True
855
+ for key in key_inputs:
856
+ # never overwrite a key with a dependency on a property
857
+ # for example - binding a datasource with a grain of <x>.fun should
858
+ # never override the grain of x to <fun>
859
+ if column.concept.address in (
860
+ self.environment.concepts[key].keys or set()
861
+ ):
862
+ eligible = False
863
+ if not eligible:
864
+ continue
845
865
  keys = [self.environment.concepts[grain] for grain in key_inputs]
846
866
  # target_c.purpose = Purpose.PROPERTY
847
867
  target_c.keys = set([x.address for x in keys])
@@ -1030,6 +1050,9 @@ class ParseToObjects(Transformer):
1030
1050
 
1031
1051
  def import_statement(self, args: list[str]) -> ImportStatement:
1032
1052
  start = datetime.now()
1053
+ is_file_resolver = isinstance(
1054
+ self.environment.config.import_resolver, FileSystemImportResolver
1055
+ )
1033
1056
  if len(args) == 2:
1034
1057
  alias = args[-1]
1035
1058
  cache_key = args[-1]
@@ -1043,9 +1066,7 @@ class ParseToObjects(Transformer):
1043
1066
  is_stdlib = True
1044
1067
  target = join(STDLIB_ROOT, *path) + ".preql"
1045
1068
  token_lookup: Path | str = Path(target)
1046
- elif isinstance(
1047
- self.environment.config.import_resolver, FileSystemImportResolver
1048
- ):
1069
+ elif is_file_resolver:
1049
1070
  target = join(self.environment.working_path, *path) + ".preql"
1050
1071
  # tokens + text are cached by path
1051
1072
  token_lookup = Path(target)
@@ -1125,7 +1146,13 @@ class ParseToObjects(Transformer):
1125
1146
  imps = ImportStatement(alias=alias, input_path=input_path, path=parsed_path)
1126
1147
 
1127
1148
  self.environment.add_import(
1128
- alias, new_env, Import(alias=alias, path=parsed_path)
1149
+ alias,
1150
+ new_env,
1151
+ Import(
1152
+ alias=alias,
1153
+ path=parsed_path,
1154
+ input_path=Path(target) if is_file_resolver else None,
1155
+ ),
1129
1156
  )
1130
1157
  end = datetime.now()
1131
1158
  perf_logger.debug(
@@ -1677,6 +1704,7 @@ class ParseToObjects(Transformer):
1677
1704
 
1678
1705
  @v_args(meta=True)
1679
1706
  def unnest(self, meta, args):
1707
+
1680
1708
  return self.function_factory.create_function(args, FunctionType.UNNEST, meta)
1681
1709
 
1682
1710
  @v_args(meta=True)
trilogy/parsing/render.py CHANGED
@@ -31,6 +31,7 @@ from trilogy.core.models.core import (
31
31
  DataType,
32
32
  ListType,
33
33
  ListWrapper,
34
+ MapWrapper,
34
35
  NumericType,
35
36
  TraitDataType,
36
37
  TupleWrapper,
@@ -250,6 +251,17 @@ class Renderer:
250
251
  def _(self, arg: TupleWrapper):
251
252
  return "(" + ", ".join([self.to_string(x) for x in arg]) + ")"
252
253
 
254
+ @to_string.register
255
+ def _(self, arg: MapWrapper):
256
+ def process_key_value(key, value):
257
+ return f"{self.to_string(key)}: {self.to_string(value)}"
258
+
259
+ return (
260
+ "{"
261
+ + ", ".join([process_key_value(key, value) for key, value in arg.items()])
262
+ + "}"
263
+ )
264
+
253
265
  @to_string.register
254
266
  def _(self, arg: DatePart):
255
267
  return arg.value
@@ -402,11 +402,11 @@
402
402
 
403
403
  struct_type: "struct"i "<" ((data_type | IDENTIFIER) ",")* (data_type | IDENTIFIER) ","? ">"
404
404
 
405
- list_type: ("list"i "<" data_type ">" ) | ("array"i "<" data_type ">" )
405
+ list_type: ("list"i | "array"i) "<" (data_type | IDENTIFIER) ">"
406
406
 
407
407
  numeric_type: "numeric"i "(" int_lit "," int_lit ")"
408
408
 
409
- map_type: "map"i "<" data_type "," data_type ">"
409
+ map_type: "map"i "<" (data_type | IDENTIFIER) "," (data_type | IDENTIFIER) ">"
410
410
 
411
411
  !data_type: ("string"i | "number"i | "numeric"i | "map"i | "list"i | "array"i | "any"i | "int"i | "bigint"i | "date"i | "datetime"i | "timestamp"i | "float"i | "bool"i | numeric_type | map_type | struct_type | list_type) ("::" IDENTIFIER)?
412
412