pytrilogy 0.0.2.4__py3-none-any.whl → 0.0.2.6__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.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.4
3
+ Version: 0.0.2.6
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,4 +1,4 @@
1
- trilogy/__init__.py,sha256=sspl5Npt0npzVRC6wCwiYXa0nK0E7a_biwiL2T4kbjM,290
1
+ trilogy/__init__.py,sha256=P77DaUk5fO6LhD9i2bg5poc0-ZbDHKxy6bYyR7Zxsoc,290
2
2
  trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  trilogy/constants.py,sha256=KIvi-cgU4R9urNgDdGiCsRkCrzjAfM4xGHhZb1SVy2w,881
4
4
  trilogy/engine.py,sha256=R5ubIxYyrxRExz07aZCUfrTsoXCHQ8DKFTDsobXdWdA,1102
@@ -8,7 +8,7 @@ trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  trilogy/utility.py,sha256=zM__8r29EsyDW7K9VOHz8yvZC2bXFzh7xKy3cL7GKsk,707
9
9
  trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  trilogy/core/constants.py,sha256=LL8NLvxb3HRnAjvofyLRXqQJijLcYiXAQYQzGarVD-g,128
11
- trilogy/core/enums.py,sha256=ozgw8G0s4CdbL6IUsX5nNT_ZGd4mMkoo8_EGLZXeQYY,5902
11
+ trilogy/core/enums.py,sha256=PKXAoIC4kVsMkViZ7-Qcfw8k1Q9NBoNNchv8ad6OdVc,5944
12
12
  trilogy/core/env_processor.py,sha256=l7TAB0LalxjTYJdTlcmFIkLXuyxa9lrenWLeZfa9qw0,2276
13
13
  trilogy/core/environment_helpers.py,sha256=mzBDHhdF9ssZ_-LY8CcaM_ddfJavkpRYrFImUd3cjXI,5972
14
14
  trilogy/core/ergonomics.py,sha256=w3gwXdgrxNHCuaRdyKg73t6F36tj-wIjQf47WZkHmJk,1465
@@ -16,13 +16,13 @@ trilogy/core/exceptions.py,sha256=NvV_4qLOgKXbpotgRf7c8BANDEvHxlqRPaA53IThQ2o,56
16
16
  trilogy/core/functions.py,sha256=ARJAyBjeS415-54k3G_bx807rkPZonEulMaLRxSP7vU,10371
17
17
  trilogy/core/graph_models.py,sha256=oJUMSpmYhqXlavckHLpR07GJxuQ8dZ1VbB1fB0KaS8c,2036
18
18
  trilogy/core/internal.py,sha256=jNGFHKENnbMiMCtAgsnLZYVSENDK4b5ALecXFZpTDzQ,1075
19
- trilogy/core/models.py,sha256=KVPUfupZVhQxJZCViMP7PELzvNR7WOwkjmqqgEM4oeM,137120
19
+ trilogy/core/models.py,sha256=atXBfSP_-xgY1ixIG98K2p0TLxmhh7bc6pqI9tPTZSY,137562
20
20
  trilogy/core/optimization.py,sha256=A8S9C9H5RcQcFSQLYtEEBnm-r1CW_e9GEWlLK7q3MqA,4930
21
21
  trilogy/core/query_processor.py,sha256=6JiX6YqsTgnsEkIxcmWFxJuM6kRJbXbPGRCQgi8g-B8,17941
22
22
  trilogy/core/optimizations/__init__.py,sha256=pxRzNzd2g8oRMy4f_ub5va6bNS2pd4hnyp9JBzTKc1E,300
23
23
  trilogy/core/optimizations/base_optimization.py,sha256=tWWT-xnTbnEU-mNi_isMNbywm8B9WTRsNFwGpeh3rqE,468
24
24
  trilogy/core/optimizations/inline_constant.py,sha256=kHNyc2UoaPVdYfVAPAFwnWuk4sJ_IF5faRtVcDOrBtw,1110
25
- trilogy/core/optimizations/inline_datasource.py,sha256=KiwZ4fnRnSOVK8zzdwMAkafszo3fNn5LY8xgb0X1CbI,3194
25
+ trilogy/core/optimizations/inline_datasource.py,sha256=9_jTjKZrRG_mPeQlvuKK2grSiZMMD2pqG4E9PAL9J4U,3383
26
26
  trilogy/core/optimizations/predicate_pushdown.py,sha256=iVZV_BVaL3I6wlELPOuJAiuF0heOM7bCUnNqyCxxRVw,6370
27
27
  trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  trilogy/core/processing/concept_strategies_v3.py,sha256=xCPfg1ySHP6C-u777kqnV8eqIK1X68hqwvRcw61q5X0,23968
@@ -49,14 +49,14 @@ trilogy/core/processing/nodes/select_node_v2.py,sha256=COu-WPuyabGAc3HTkJB-_7eNz
49
49
  trilogy/core/processing/nodes/unnest_node.py,sha256=JFtm90IVM-46aCYkTNIaJah6v9ApAfonjVhcVM1HmDE,1903
50
50
  trilogy/core/processing/nodes/window_node.py,sha256=X7qxLUKd3tekjUUsmH_4vz5b-U89gMnGd04VBxuu2Ns,1280
51
51
  trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
- trilogy/dialect/base.py,sha256=aE8iJJhkGtZE04mYm_wQx3ebQKYbcRsKISUd9mbrZ2A,28782
52
+ trilogy/dialect/base.py,sha256=QQBUhR53KvwW1Xac1cH4NAvZNnNR2HOoo-lOOUar3G4,28889
53
53
  trilogy/dialect/bigquery.py,sha256=15KJ-cOpBlk9O7FPviPgmg8xIydJeKx7WfmL3SSsPE8,2953
54
- trilogy/dialect/common.py,sha256=5jdOHWIj3Xv8F8y5mnyWHLjxD_we2fncM-ZnNetJP7U,2781
54
+ trilogy/dialect/common.py,sha256=1oBJ44P4Dw1bMeek-wmZVwcflVYHbAiKb1ByN-iFXVk,2988
55
55
  trilogy/dialect/config.py,sha256=tLVEMctaTDhUgARKXUNfHUcIolGaALkQ0RavUvXAY4w,2994
56
- trilogy/dialect/duckdb.py,sha256=JEdhctDxZr-FEodot5V4De6JnrQDJT02zbjX48j6ay8,3125
56
+ trilogy/dialect/duckdb.py,sha256=PHDNekefyUgdtpdJS-MVZQgfaich5pSec0e50IVPMMw,3124
57
57
  trilogy/dialect/enums.py,sha256=4NdpsydBpDn6jnh0JzFz5VvQEtnShErWtWHVyT6TNpw,3948
58
58
  trilogy/dialect/postgres.py,sha256=ev1RJZsC8BB3vJSxJ4q-TTYqZ4Hk1NXUtuRkLrQEBX0,3254
59
- trilogy/dialect/presto.py,sha256=InvHDXsGLDG3E3ffl7hGZ-9y3vH8RO6UhNE0OSra01g,3269
59
+ trilogy/dialect/presto.py,sha256=ohWpG7-AWEUGQEpmnJbqVeGRDISX1u18DI-6GjYjwRA,3350
60
60
  trilogy/dialect/snowflake.py,sha256=_Bf4XO7-nImMv9XCSsTfVM3g2f_KHdO17VTa9J-HgSM,2989
61
61
  trilogy/dialect/sql_server.py,sha256=OtXbm1v6NIGyXeC5i18ojUvLeqescc_Pbv3EASUBB94,3074
62
62
  trilogy/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -69,14 +69,14 @@ trilogy/parsing/common.py,sha256=aYmLtsEk8VJj5yYASzUcTf0i0lXVsZBbZJwphwokxK4,609
69
69
  trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
70
70
  trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
71
71
  trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
72
- trilogy/parsing/parse_engine.py,sha256=_Cpq7bq6QyuDcnmYX_fDLGNFNSQPj173cqYsh3V6jH8,60969
72
+ trilogy/parsing/parse_engine.py,sha256=cIoQuDGaCx_HYgNjOvG6GDVGOu5pcLBylZsK-wy5r6Q,61098
73
73
  trilogy/parsing/render.py,sha256=Gy_6wVYPwYLf35Iota08sbqveuWILtUhI8MYStcvtJM,12174
74
- trilogy/parsing/trilogy.lark,sha256=uQlyb4RQ6Tpp8lRlXLK_Qb8sD1qzYVYErRI97v22Zak,11317
74
+ trilogy/parsing/trilogy.lark,sha256=cUcwxUTlxU7jKFzYEXYARLTsPHG5cVLk-Xhltw6m2lY,11357
75
75
  trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
76
  trilogy/scripts/trilogy.py,sha256=PHxvv6f2ODv0esyyhWxlARgra8dVhqQhYl0lTrSyVNo,3729
77
- pytrilogy-0.0.2.4.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
78
- pytrilogy-0.0.2.4.dist-info/METADATA,sha256=R3D4ULv2Y2yJRO0sToJ5FlPPiqkU8H02X035M-vaG9s,7906
79
- pytrilogy-0.0.2.4.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
80
- pytrilogy-0.0.2.4.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
81
- pytrilogy-0.0.2.4.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
82
- pytrilogy-0.0.2.4.dist-info/RECORD,,
77
+ pytrilogy-0.0.2.6.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
78
+ pytrilogy-0.0.2.6.dist-info/METADATA,sha256=W8sJtJY77xavjIKHsL75YdHtxiXFJljB_z6a6xeWjH0,7906
79
+ pytrilogy-0.0.2.6.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
80
+ pytrilogy-0.0.2.6.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
81
+ pytrilogy-0.0.2.6.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
82
+ pytrilogy-0.0.2.6.dist-info/RECORD,,
trilogy/__init__.py CHANGED
@@ -4,6 +4,6 @@ from trilogy.executor import Executor
4
4
  from trilogy.parser import parse
5
5
  from trilogy.constants import CONFIG
6
6
 
7
- __version__ = "0.0.2.4"
7
+ __version__ = "0.0.2.6"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
trilogy/core/enums.py CHANGED
@@ -7,6 +7,7 @@ class UnnestMode(Enum):
7
7
  DIRECT = "direct"
8
8
  CROSS_APPLY = "cross_apply"
9
9
  CROSS_JOIN = "cross_join"
10
+ CROSS_JOIN_ALIAS = "cross_join_alias"
10
11
 
11
12
 
12
13
  class ConceptSource(Enum):
trilogy/core/models.py CHANGED
@@ -1817,6 +1817,7 @@ class MultiSelectStatement(SelectTypeMixin, Mergeable, Namespaced, BaseModel):
1817
1817
  class Address(BaseModel):
1818
1818
  location: str
1819
1819
  is_query: bool = False
1820
+ quoted: bool = False
1820
1821
 
1821
1822
 
1822
1823
  class Query(BaseModel):
@@ -2433,7 +2434,13 @@ class CTE(BaseModel):
2433
2434
  # if we've entirely removed the need to join to someplace to get the concept
2434
2435
  # drop the join as well.
2435
2436
  for removed_cte in removed:
2436
- still_required = any([removed_cte in x for x in self.source_map.values()])
2437
+ still_required = any(
2438
+ [
2439
+ removed_cte in x
2440
+ for x in self.source_map.values()
2441
+ or self.existence_source_map.values()
2442
+ ]
2443
+ )
2437
2444
  if not still_required:
2438
2445
  self.joins = [
2439
2446
  join
@@ -2451,6 +2458,7 @@ class CTE(BaseModel):
2451
2458
  candidates = [x.name for x in self.parent_ctes]
2452
2459
  self.base_name_override = candidates[0] if candidates else None
2453
2460
  self.base_alias_override = candidates[0] if candidates else None
2461
+ return True
2454
2462
 
2455
2463
  def inline_parent_datasource(self, parent: CTE, force_group: bool = False) -> bool:
2456
2464
  qds_being_inlined = parent.source
@@ -2572,6 +2580,16 @@ class CTE(BaseModel):
2572
2580
  return self.relevant_base_ctes[0].name
2573
2581
  return self.source.name
2574
2582
 
2583
+ @property
2584
+ def quote_address(self) -> bool:
2585
+ if self.is_root_datasource:
2586
+ candidate = self.source.datasources[0]
2587
+ if isinstance(candidate, Datasource) and isinstance(
2588
+ candidate.address, Address
2589
+ ):
2590
+ return candidate.address.quoted
2591
+ return False
2592
+
2575
2593
  @property
2576
2594
  def base_alias(self) -> str:
2577
2595
  if self.base_alias_override:
@@ -3315,7 +3333,7 @@ class Comparison(
3315
3333
  and self.operator == other.operator
3316
3334
  )
3317
3335
 
3318
- def inline_constant(self, constant: Concept) -> "Comparison":
3336
+ def inline_constant(self, constant: Concept):
3319
3337
  assert isinstance(constant.lineage, Function)
3320
3338
  new_val = constant.lineage.arguments[0]
3321
3339
  if isinstance(self.left, ConstantInlineable):
@@ -3332,10 +3350,7 @@ class Comparison(
3332
3350
  else:
3333
3351
  new_right = self.right
3334
3352
 
3335
- if self.right == constant:
3336
- new_right = new_val
3337
-
3338
- return Comparison(
3353
+ return self.__class__(
3339
3354
  left=new_left,
3340
3355
  right=new_right,
3341
3356
  operator=self.operator,
@@ -43,10 +43,14 @@ class InlineDatasource(OptimizationRule):
43
43
  continue
44
44
  root_outputs = {x.address for x in root.output_concepts}
45
45
  cte_outputs = {x.address for x in cte.output_columns}
46
+ inherited = {x for x, v in cte.source_map.items() if v}
46
47
  # cte_inherited_outputs = {x.address for x in parent_cte.output_columns if parent_cte.source_map.get(x.address)}
47
48
  grain_components = {x.address for x in root.grain.components}
48
- if not cte_outputs.issubset(root_outputs):
49
- self.log(f"Not all {parent_cte.name} outputs are found on datasource")
49
+ if not inherited.issubset(root_outputs):
50
+ cte_missing = inherited - root_outputs
51
+ self.log(
52
+ f"Not all {parent_cte.name} require inputs are found on datasource, missing {cte_missing}"
53
+ )
50
54
  continue
51
55
  if not grain_components.issubset(cte_outputs):
52
56
  self.log("Not all datasource components in cte outputs, forcing group")
trilogy/dialect/base.py CHANGED
@@ -188,10 +188,11 @@ FUNCTION_GRAIN_MATCH_MAP = {
188
188
  GENERIC_SQL_TEMPLATE = Template(
189
189
  """{%- if ctes %}
190
190
  WITH {% for cte in ctes %}
191
- {{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
191
+ {{cte.name}} as (
192
+ {{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
192
193
  {%- if full_select -%}
193
194
  {{full_select}}
194
- {%- else -%}
195
+ {% else -%}
195
196
  SELECT
196
197
  {%- if limit is not none %}
197
198
  TOP {{ limit }}{% endif %}
@@ -498,10 +499,12 @@ class BaseDialect:
498
499
  for c in cte.output_columns
499
500
  if c.address not in [y.address for y in cte.hidden_concepts]
500
501
  ]
501
- if cte.base_name == cte.base_alias:
502
- source = cte.base_name
502
+ if cte.quote_address:
503
+ source = f"{self.QUOTE_CHARACTER}{cte.base_name}{self.QUOTE_CHARACTER}"
503
504
  else:
504
- source = f"{cte.base_name} as {cte.base_alias}"
505
+ source = cte.base_name
506
+ if cte.base_name != cte.base_alias:
507
+ source = f"{source} as {cte.base_alias}"
505
508
  return CompiledCTE(
506
509
  name=cte.name,
507
510
  statement=self.SQL_TEMPLATE.render(
trilogy/dialect/common.py CHANGED
@@ -26,7 +26,8 @@ def render_join(
26
26
  raise ValueError("must provide a cte to build an unnest joins")
27
27
  if unnest_mode == UnnestMode.CROSS_JOIN:
28
28
  return f"CROSS JOIN {render_func(join.concept, cte, False)} as {quote_character}{join.concept.safe_address}{quote_character}"
29
-
29
+ if unnest_mode == UnnestMode.CROSS_JOIN_ALIAS:
30
+ return f"CROSS JOIN {render_func(join.concept, cte, False)} as array_unnest ({quote_character}{join.concept.safe_address}{quote_character})"
30
31
  return f"FULL JOIN {render_func(join.concept, cte, False)} as unnest_wrapper({quote_character}{join.concept.safe_address}{quote_character})"
31
32
  left_name = join.left_name
32
33
  right_name = join.right_name
trilogy/dialect/duckdb.py CHANGED
@@ -49,7 +49,6 @@ WITH {% for cte in ctes %}
49
49
  {{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
50
50
  {% if full_select -%}{{full_select}}
51
51
  {% else -%}
52
-
53
52
  SELECT
54
53
  {%- for select in select_columns %}
55
54
  {{ select }}{% if not loop.last %},{% endif %}{% endfor %}
trilogy/dialect/presto.py CHANGED
@@ -5,7 +5,7 @@ from jinja2 import Template
5
5
  from trilogy.core.enums import FunctionType, WindowType
6
6
  from trilogy.dialect.base import BaseDialect
7
7
  from trilogy.core.models import DataType
8
-
8
+ from trilogy.core.enums import UnnestMode
9
9
 
10
10
  WINDOW_FUNCTION_MAP: Mapping[WindowType, Callable[[Any, Any, Any], str]] = {}
11
11
 
@@ -86,6 +86,7 @@ class PrestoDialect(BaseDialect):
86
86
  QUOTE_CHARACTER = '"'
87
87
  SQL_TEMPLATE = SQL_TEMPLATE
88
88
  DATATYPE_MAP = {**BaseDialect.DATATYPE_MAP, DataType.NUMERIC: "DECIMAL"}
89
+ UNNEST_MODE = UnnestMode.CROSS_JOIN
89
90
 
90
91
 
91
92
  class TrinoDialect(PrestoDialect):
@@ -297,8 +297,11 @@ class ParseToObjects(Transformer):
297
297
  def concept_lit(self, args) -> Concept:
298
298
  return self.environment.concepts.__getitem__(args[0])
299
299
 
300
- def ADDRESS(self, args) -> str:
301
- return args.value
300
+ def ADDRESS(self, args) -> Address:
301
+ return Address(location=args.value, quoted=False)
302
+
303
+ def QUOTED_ADDRESS(self, args) -> Address:
304
+ return Address(location=args.value[1:-1], quoted=True)
302
305
 
303
306
  def STRING_CHARS(self, args) -> str:
304
307
  return args.value
@@ -1010,7 +1013,7 @@ class ParseToObjects(Transformer):
1010
1013
 
1011
1014
  @v_args(meta=True)
1012
1015
  def address(self, meta: Meta, args):
1013
- return Address(location=args[0])
1016
+ return args[0]
1014
1017
 
1015
1018
  @v_args(meta=True)
1016
1019
  def query(self, meta: Meta, args):
@@ -39,7 +39,7 @@
39
39
 
40
40
  grain_clause: "grain" "(" column_list ")"
41
41
 
42
- address: "address" ADDRESS
42
+ address: "address" (QUOTED_ADDRESS | ADDRESS)
43
43
 
44
44
  query: "query" MULTILINE_STRING
45
45
 
@@ -258,7 +258,8 @@
258
258
  // base language constructs
259
259
  concept_lit: IDENTIFIER
260
260
  IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_\-\.\-]*/
261
- ADDRESS: IDENTIFIER | /`[a-zA-Z\_][a-zA-Z0-9\_\-\.\-\*]*`/
261
+ QUOTED_ADDRESS: /`[a-zA-Z\_][a-zA-Z0-9\_\-\.\-\*\:]*`/
262
+ ADDRESS: IDENTIFIER
262
263
 
263
264
  MULTILINE_STRING: /\'{3}(.*?)\'{3}/s
264
265