pytrilogy 0.0.3.1__py3-none-any.whl → 0.0.3.2__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.2
2
2
  Name: pytrilogy
3
- Version: 0.0.3.1
3
+ Version: 0.0.3.2
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=KLYdc650VOIzjRZdH4baZEtSeBPReI5LTzjij3xENfE,302
1
+ trilogy/__init__.py,sha256=u47oSxZB2gBUiSNHosZXaS5uhP_ad0NBnUKDXu5UQMw,302
2
2
  trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  trilogy/constants.py,sha256=qZ1d0hoKPPV2HHCoFwPYTVB7b6bXjpWvXd3lE-zEhy8,1494
4
4
  trilogy/engine.py,sha256=yOPnR7XCjWG82Gym_LLZBkYKKJdLCvqdCyt8zguNcnM,1103
@@ -32,10 +32,10 @@ trilogy/core/optimizations/inline_constant.py,sha256=lvNTIXaLNkw3HseJyXyDNk5R52d
32
32
  trilogy/core/optimizations/inline_datasource.py,sha256=AHuTGh2x0GQ8usOe0NiFncfTFQ_KogdgDl4uucmhIbI,4241
33
33
  trilogy/core/optimizations/predicate_pushdown.py,sha256=g4AYE8Aw_iMlAh68TjNXGP754NTurrDduFECkUjoBnc,9399
34
34
  trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- trilogy/core/processing/concept_strategies_v3.py,sha256=hzClGgWxfqxnx4Nc0iIr_DwOybAnuk5RozCijqsZUg4,39168
35
+ trilogy/core/processing/concept_strategies_v3.py,sha256=0rFnasSQlkXTRIfAFMHyHVux1pTQ3ryKeQds-SFSot0,40290
36
36
  trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuXl-Te85lag,1205
37
37
  trilogy/core/processing/utility.py,sha256=Oc5tLGeDDpzhbfo2ZcF8ex1kez-NcJDMcG2Lm5BjS4c,20548
38
- trilogy/core/processing/node_generators/__init__.py,sha256=s_YV1OYc336DuS9591259qjI_K_CtOCuhkf4t2aOgYs,733
38
+ trilogy/core/processing/node_generators/__init__.py,sha256=o8rOFHPSo-s_59hREwXMW6gjUJCsiXumdbJNozHUf-Y,800
39
39
  trilogy/core/processing/node_generators/basic_node.py,sha256=UVsXMn6jTjm_ofVFt218jAS11s4RV4zD781vP4im-GI,3371
40
40
  trilogy/core/processing/node_generators/common.py,sha256=ZsDzThjm_mAtdQpKAg8QIJiPVZ4KuUkKyilj4eOhSDs,9439
41
41
  trilogy/core/processing/node_generators/filter_node.py,sha256=rlY7TbgjJlGhahYgdCIJpJbaSREAGVJEsyUIGaA38O0,8271
@@ -43,9 +43,10 @@ trilogy/core/processing/node_generators/group_node.py,sha256=94uoGZWvBKJ1eqjbDHC
43
43
  trilogy/core/processing/node_generators/group_to_node.py,sha256=E5bEjovSx422d_MlAUCDFdY4P2WJVp61BmWwltkhzA8,3095
44
44
  trilogy/core/processing/node_generators/multiselect_node.py,sha256=z9FQOxxUvxW31a0TckFfAvnuvU8vP1GyN224RTbXUAk,7114
45
45
  trilogy/core/processing/node_generators/node_merge_node.py,sha256=sv55oynfqgpHEpo1OEtVDri-5fywzPhDlR85qaWikvY,16195
46
- trilogy/core/processing/node_generators/rowset_node.py,sha256=lVjwxO71HVZ5De82bo7YYILVQ7cYeXdCzIloLCfpyLE,6112
46
+ trilogy/core/processing/node_generators/rowset_node.py,sha256=8yeMWiyi9IFnza7qPn9YYC3WpA53weq3AY5WisIui8Y,6705
47
47
  trilogy/core/processing/node_generators/select_merge_node.py,sha256=VHCPMbnKfg7AOfoYa6PKxpNni-j5JEfliNUiltmZhds,19698
48
48
  trilogy/core/processing/node_generators/select_node.py,sha256=Y-zO0AFkTrpi2LyebjpyHU7WWANr7nKZSS9rY7DH4Wo,1888
49
+ trilogy/core/processing/node_generators/synonym_node.py,sha256=9LHK2XHDjbyTLjmDQieskG8fqbiSpRnFOkfrutDnOTE,2258
49
50
  trilogy/core/processing/node_generators/union_node.py,sha256=zuMSmgF170vzlp2BBQEhKbqUMjVl2xQDqUB82Dhv-VU,2536
50
51
  trilogy/core/processing/node_generators/unnest_node.py,sha256=cOEKnMRzXUW3bwmiOlgn3E1-B38osng0dh2pDykwITY,2410
51
52
  trilogy/core/processing/node_generators/window_node.py,sha256=6KoxhmpVOTN3HGWT0FIS96xqlm2Inouw9VL2D_0kSg0,3481
@@ -66,7 +67,7 @@ trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
66
67
  trilogy/core/statements/common.py,sha256=KxEmz2ySySyZ6CTPzn0fJl5NX2KOk1RPyuUSwWhnK1g,759
67
68
  trilogy/core/statements/execute.py,sha256=cSlvpHFOqpiZ89pPZ5GDp9Hu6j6uj-5_h21FWm_L-KM,1248
68
69
  trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
- trilogy/dialect/base.py,sha256=H6eebtFV09edKbBpukxxB0xBftolXFhvXbshbZhNeUI,40290
70
+ trilogy/dialect/base.py,sha256=u00kIIl98as1QzcduiiyyoBzxRGVeBxfeO5hWlRCAJU,40222
70
71
  trilogy/dialect/bigquery.py,sha256=mKC3zoEU232h9RtIXJjqiZ72lWH8a6S28p6wAZKrAfg,2952
71
72
  trilogy/dialect/common.py,sha256=cbTo_vamdp8pj9spSjGSH-bSZpy4FpNJ12k5vMvyT2Y,3942
72
73
  trilogy/dialect/config.py,sha256=UiBY2tBbNk9owx-zxP_3lN9lErEUXhXIU_bcXA18AvU,2992
@@ -82,7 +83,7 @@ trilogy/hooks/graph_hook.py,sha256=c-vC-IXoJ_jDmKQjxQyIxyXPOuUcLIURB573gCsAfzQ,2
82
83
  trilogy/hooks/query_debugger.py,sha256=1npRjww94sPV5RRBBlLqMJRaFkH9vhEY6o828MeoEcw,5583
83
84
  trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
85
  trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
- trilogy/parsing/common.py,sha256=b_TGP-cSV21Y8INI77DhD3c4kg0vpJ5TZUuzU-JcdaU,20594
86
+ trilogy/parsing/common.py,sha256=lZSSu6Q8HvJVB0xCYsNpoTlkCgNNEVVCwHExVBWCDO8,20524
86
87
  trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
87
88
  trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
88
89
  trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
@@ -91,9 +92,9 @@ trilogy/parsing/render.py,sha256=o_XuQWhcwx1lD9eGVqkqZEwkmQK0HdmWWokGBtdeH4I,178
91
92
  trilogy/parsing/trilogy.lark,sha256=EazfEvYPuvkPkNjUnVzFi0uD9baavugbSI8CyfawShk,12573
92
93
  trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
94
  trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
94
- pytrilogy-0.0.3.1.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
95
- pytrilogy-0.0.3.1.dist-info/METADATA,sha256=HAkTZhYCZL5M6_WSWKakxXEHP5xqxSMMgKhM2E6HndE,8983
96
- pytrilogy-0.0.3.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
97
- pytrilogy-0.0.3.1.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
98
- pytrilogy-0.0.3.1.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
99
- pytrilogy-0.0.3.1.dist-info/RECORD,,
95
+ pytrilogy-0.0.3.2.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
96
+ pytrilogy-0.0.3.2.dist-info/METADATA,sha256=buA8GqwIT8kSii6cguT2dMcbI12XMVwQDoQya9jo-Gc,8983
97
+ pytrilogy-0.0.3.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
98
+ pytrilogy-0.0.3.2.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
99
+ pytrilogy-0.0.3.2.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
100
+ pytrilogy-0.0.3.2.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.1"
7
+ __version__ = "0.0.3.2"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -24,6 +24,7 @@ from trilogy.core.processing.node_generators import (
24
24
  gen_merge_node,
25
25
  gen_multiselect_node,
26
26
  gen_rowset_node,
27
+ gen_synonym_node,
27
28
  gen_union_node,
28
29
  gen_unnest_node,
29
30
  gen_window_node,
@@ -364,7 +365,7 @@ def generate_node(
364
365
  x.address for x in local_optional if x.derivation != Derivation.CONSTANT
365
366
  ]
366
367
  logger.info(
367
- f"{depth_to_prefix(depth)}{LOGGER_PREFIX} including filter concepts, there is non root filter requirements {non_root}. Recursing with all of these as mandatory"
368
+ f"{depth_to_prefix(depth)}{LOGGER_PREFIX} including filter concepts, there are non root/non constant concepts we should find first: {non_root}. Recursing with all of these as mandatory"
368
369
  )
369
370
 
370
371
  if not history.check_started(
@@ -448,7 +449,7 @@ def generate_node(
448
449
  if x.derivation not in (Derivation.ROOT, Derivation.CONSTANT)
449
450
  ]
450
451
  logger.info(
451
- f"{depth_to_prefix(depth)}{LOGGER_PREFIX} including filter concepts, there is non root filter requirements {non_root}. Recursing with all of these as mandatory"
452
+ f"{depth_to_prefix(depth)}{LOGGER_PREFIX} including any filters, there are non-root concepts we should expand first: {non_root}. Recursing with all of these as mandatory"
452
453
  )
453
454
 
454
455
  if not history.check_started(
@@ -491,7 +492,7 @@ def generate_node(
491
492
  all_concepts=root_targets,
492
493
  environment=environment,
493
494
  g=g,
494
- depth=depth,
495
+ depth=depth + 1,
495
496
  source_concepts=source_concepts,
496
497
  history=history,
497
498
  search_conditions=conditions,
@@ -516,9 +517,35 @@ def generate_node(
516
517
  f"{depth_to_prefix(depth)}{LOGGER_PREFIX} Found connections for {[c.address for c in root_targets]} via concept addition; removing extra {[c.address for c in extra]}"
517
518
  )
518
519
  return expanded
520
+
521
+ logger.info(
522
+ f"{depth_to_prefix(depth)}{LOGGER_PREFIX} could not find additional concept(s) to inject"
523
+ )
519
524
  logger.info(
520
- f"{depth_to_prefix(depth)}{LOGGER_PREFIX} could not find additional concept to inject"
525
+ f"{depth_to_prefix(depth)}{LOGGER_PREFIX} Could not resolve root concepts, checking for synonyms"
521
526
  )
527
+ if not history.check_started(
528
+ root_targets, accept_partial=accept_partial, conditions=conditions
529
+ ):
530
+ history.log_start(
531
+ root_targets, accept_partial=accept_partial, conditions=conditions
532
+ )
533
+ resolved = gen_synonym_node(
534
+ all_concepts=root_targets,
535
+ environment=environment,
536
+ g=g,
537
+ depth=depth + 1,
538
+ source_concepts=source_concepts,
539
+ history=history,
540
+ conditions=conditions,
541
+ accept_partial=accept_partial,
542
+ )
543
+ if resolved:
544
+ logger.info(
545
+ f"{depth_to_prefix(depth)}{LOGGER_PREFIX} resolved concepts through synonyms"
546
+ )
547
+ return resolved
548
+
522
549
  return None
523
550
  else:
524
551
  raise ValueError(f"Unknown derivation {concept.derivation} on {concept}")
@@ -6,6 +6,7 @@ from .multiselect_node import gen_multiselect_node
6
6
  from .node_merge_node import gen_merge_node
7
7
  from .rowset_node import gen_rowset_node
8
8
  from .select_node import gen_select_node
9
+ from .synonym_node import gen_synonym_node
9
10
  from .union_node import gen_union_node
10
11
  from .unnest_node import gen_unnest_node
11
12
  from .window_node import gen_window_node
@@ -22,4 +23,5 @@ __all__ = [
22
23
  "gen_group_to_node",
23
24
  "gen_rowset_node",
24
25
  "gen_multiselect_node",
26
+ "gen_synonym_node",
25
27
  ]
@@ -51,10 +51,17 @@ def gen_rowset_node(
51
51
  enrichment = set([x.address for x in local_optional])
52
52
 
53
53
  factory = Factory(environment=history.base_environment, grain=select.grain)
54
+ logger.info(
55
+ f"{padding(depth)}{LOGGER_PREFIX} rowset derived concepts are {lineage.rowset.derived_concepts}"
56
+ )
57
+ concept_pool = list(environment.concepts.values()) + list(
58
+ environment.alias_origin_lookup.values()
59
+ )
60
+ rowset_outputs = [
61
+ x.address for x in concept_pool if x.address in lineage.rowset.derived_concepts
62
+ ]
54
63
  rowset_relevant: list[BuildConcept] = [
55
- v
56
- for v in environment.concepts.values()
57
- if v.address in lineage.rowset.derived_concepts
64
+ v for v in concept_pool if v.address in rowset_outputs
58
65
  ]
59
66
 
60
67
  select_hidden = node.hidden_concepts
@@ -69,7 +76,7 @@ def gen_rowset_node(
69
76
  factory.build(x) for x in select.output_components if x.address in enrichment
70
77
  ]
71
78
  # add in other other concepts
72
- node.add_output_concepts(rowset_relevant + additional_relevant)
79
+ node.set_output_concepts(rowset_relevant + additional_relevant)
73
80
  if select.where_clause:
74
81
  for item in additional_relevant:
75
82
  logger.info(
@@ -82,7 +89,9 @@ def gen_rowset_node(
82
89
  for x in node.output_concepts
83
90
  if x.address not in local_optional + [concept]
84
91
  and x.derivation != Derivation.ROWSET
92
+ and not any(z in lineage.rowset.derived_concepts for z in x.pseudonyms)
85
93
  ]
94
+ logger.info(f"{padding(depth)}{LOGGER_PREFIX} hiding {final_hidden}")
86
95
  node.hide_output_concepts(final_hidden)
87
96
  assert node.resolution_cache
88
97
  # assume grain to be output of select
@@ -102,6 +111,9 @@ def gen_rowset_node(
102
111
  )
103
112
 
104
113
  node.rebuild_cache()
114
+ logger.info(
115
+ f"{padding(depth)}{LOGGER_PREFIX} final output is {[x.address for x in node.output_concepts]}"
116
+ )
105
117
  if not local_optional or all(
106
118
  x.address in node.output_concepts and x.address not in node.partial_concepts
107
119
  for x in local_optional
@@ -0,0 +1,68 @@
1
+ import itertools
2
+ from collections import defaultdict
3
+ from typing import List
4
+
5
+ from trilogy.constants import logger
6
+ from trilogy.core.enums import FunctionType
7
+ from trilogy.core.models.build import BuildConcept, BuildFunction, BuildWhereClause
8
+ from trilogy.core.models.build_environment import BuildEnvironment
9
+ from trilogy.core.processing.nodes import History, StrategyNode
10
+ from trilogy.core.processing.utility import padding
11
+
12
+ LOGGER_PREFIX = "[GEN_SYNONYM_NODE]"
13
+
14
+
15
+ def is_union(c: BuildConcept):
16
+ return (
17
+ isinstance(c.lineage, BuildFunction)
18
+ and c.lineage.operator == FunctionType.UNION
19
+ )
20
+
21
+
22
+ def gen_synonym_node(
23
+ all_concepts: List[BuildConcept],
24
+ environment: BuildEnvironment,
25
+ g,
26
+ depth: int,
27
+ source_concepts,
28
+ history: History | None = None,
29
+ conditions: BuildWhereClause | None = None,
30
+ accept_partial: bool = False,
31
+ ) -> StrategyNode | None:
32
+ local_prefix = f"[GEN_SYNONYM_NODE] {padding(depth)}"
33
+ base_fingerprint = tuple([x.address for x in all_concepts])
34
+ synonyms = defaultdict(list)
35
+ synonym_count = 0
36
+ for x in all_concepts:
37
+ synonyms[x.address] = [x]
38
+ for y in x.pseudonyms:
39
+
40
+ if y in environment.alias_origin_lookup:
41
+ synonyms[x.address].append(environment.alias_origin_lookup[y])
42
+ synonym_count += 1
43
+ elif y in environment.concepts:
44
+ synonyms[x.address].append(environment.concepts[y])
45
+ synonym_count += 1
46
+ if synonym_count == 0:
47
+ return None
48
+
49
+ logger.info(f"{local_prefix} Generating Synonym Node with {len(synonyms)} synonyms")
50
+
51
+ combinations = itertools.product(*(synonyms[obj] for obj in synonyms.keys()))
52
+ for combo in combinations:
53
+ fingerprint = tuple([x.address for x in combo])
54
+ if fingerprint == base_fingerprint:
55
+ continue
56
+ attempt: StrategyNode | None = source_concepts(
57
+ combo,
58
+ history=history,
59
+ environment=environment,
60
+ depth=depth,
61
+ conditions=conditions,
62
+ g=g,
63
+ accept_partial=accept_partial,
64
+ )
65
+ if attempt:
66
+ logger.info(f"{local_prefix} found inputs with {combo}")
67
+ return attempt
68
+ return None
trilogy/dialect/base.py CHANGED
@@ -295,7 +295,9 @@ class BaseDialect:
295
295
 
296
296
  # return f"{cte.name}.{self.QUOTE_CHARACTER}{order_item.expr.safe_address}{self.QUOTE_CHARACTER} {order_item.order.value}"
297
297
 
298
- return f"{self.render_expr(order_item.expr, cte=cte, qualify=False)} {order_item.order.value}"
298
+ return (
299
+ f"{self.render_expr(order_item.expr, cte=cte, )} {order_item.order.value}"
300
+ )
299
301
 
300
302
  def render_concept_sql(
301
303
  self,
@@ -303,7 +305,6 @@ class BaseDialect:
303
305
  cte: CTE | UnionCTE,
304
306
  alias: bool = True,
305
307
  raise_invalid: bool = False,
306
- qualify: bool = True,
307
308
  ) -> str:
308
309
  result = None
309
310
  if c.pseudonyms:
@@ -317,7 +318,9 @@ class BaseDialect:
317
318
  f"{LOGGER_PREFIX} [{c.address}] Attempting rendering w/ candidate {candidate.address}"
318
319
  )
319
320
  result = self._render_concept_sql(
320
- candidate, cte, raise_invalid=True, qualify=qualify
321
+ candidate,
322
+ cte,
323
+ raise_invalid=True,
321
324
  )
322
325
  if result:
323
326
  break
@@ -325,7 +328,9 @@ class BaseDialect:
325
328
  continue
326
329
  if not result:
327
330
  result = self._render_concept_sql(
328
- c, cte, raise_invalid=raise_invalid, qualify=qualify
331
+ c,
332
+ cte,
333
+ raise_invalid=raise_invalid,
329
334
  )
330
335
  if alias:
331
336
  return f"{result} as {self.QUOTE_CHARACTER}{c.safe_address}{self.QUOTE_CHARACTER}"
@@ -336,7 +341,6 @@ class BaseDialect:
336
341
  c: BuildConcept,
337
342
  cte: CTE | UnionCTE,
338
343
  raise_invalid: bool = False,
339
- qualify: bool = True,
340
344
  ) -> str:
341
345
  # only recurse while it's in sources of the current cte
342
346
  logger.debug(
@@ -515,7 +519,6 @@ class BaseDialect:
515
519
  cte: Optional[CTE | UnionCTE] = None,
516
520
  cte_map: Optional[Dict[str, CTE | UnionCTE]] = None,
517
521
  raise_invalid: bool = False,
518
- qualify: bool = True,
519
522
  ) -> str:
520
523
  if isinstance(e, SUBSELECT_COMPARISON_ITEMS):
521
524
  if isinstance(e.right, BuildConcept):
@@ -639,7 +642,6 @@ class BaseDialect:
639
642
  cte,
640
643
  alias=False,
641
644
  raise_invalid=raise_invalid,
642
- qualify=qualify,
643
645
  )
644
646
  elif cte_map:
645
647
  return f"{cte_map[e.address].name}.{self.QUOTE_CHARACTER}{e.safe_address}{self.QUOTE_CHARACTER}"
trilogy/parsing/common.py CHANGED
@@ -539,9 +539,6 @@ def rowset_to_concepts(rowset: RowsetDerivationStatement, environment: Environme
539
539
  default_grain = Grain.from_concepts([*pre_output])
540
540
  # remap everything to the properties of the rowset
541
541
  for x in pre_output:
542
- print("xxxx")
543
- print(x.address)
544
- print(x.grain)
545
542
  if x.keys:
546
543
  if all([k in orig for k in x.keys]):
547
544
  x.keys = set([orig[k].address if k in orig else k for k in x.keys])