pytrilogy 0.0.3.44__py3-none-any.whl → 0.0.3.46__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.44
3
+ Version: 0.0.3.46
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.44.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
- trilogy/__init__.py,sha256=_DLr4-giprU_EZXs7LGyoeoIt8LaSs4wYqbdAajaVr4,303
1
+ pytrilogy-0.0.3.46.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
+ trilogy/__init__.py,sha256=YyUmUiX_R4JnFfmRIfKopul0PlCDdGcFxMRUxiSPkp0,303
3
3
  trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  trilogy/constants.py,sha256=5eQxk1A0pv-TQk3CCvgZCFA9_K-6nxrOm7E5Lxd7KIY,1652
5
5
  trilogy/engine.py,sha256=OK2RuqCIUId6yZ5hfF8J1nxGP0AJqHRZiafcowmW0xc,1728
@@ -22,7 +22,7 @@ trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
22
22
  trilogy/core/optimization.py,sha256=aihzx4-2-mSjx5td1TDTYGvc7e9Zvy-_xEyhPqLS-Ig,8314
23
23
  trilogy/core/query_processor.py,sha256=Vl-u0F0rbqI2liv82yJgiZCB255Kx_KiuzZVHL6aeTM,19459
24
24
  trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- trilogy/core/models/author.py,sha256=7lPUVm1uCXYsyV85p34AvtLLzHqDGCeesTUT8ZHCwo4,76859
25
+ trilogy/core/models/author.py,sha256=b5GQc79w-gFQfZhgBdHeRCJAtCYr4j_da6k3Dkx4YAA,76863
26
26
  trilogy/core/models/build.py,sha256=EsI7BLmFXdxj1an3NnKR_Qm79tcjlFKjmLjmt3_v2eA,61829
27
27
  trilogy/core/models/build_environment.py,sha256=s_C9xAHuD3yZ26T15pWVBvoqvlp2LdZ8yjsv2_HdXLk,5363
28
28
  trilogy/core/models/core.py,sha256=wx6hJcFECMG-Ij972ADNkr-3nFXkYESr82ObPiC46_U,10875
@@ -35,13 +35,13 @@ trilogy/core/optimizations/inline_constant.py,sha256=lvNTIXaLNkw3HseJyXyDNk5R52d
35
35
  trilogy/core/optimizations/inline_datasource.py,sha256=AHuTGh2x0GQ8usOe0NiFncfTFQ_KogdgDl4uucmhIbI,4241
36
36
  trilogy/core/optimizations/predicate_pushdown.py,sha256=g4AYE8Aw_iMlAh68TjNXGP754NTurrDduFECkUjoBnc,9399
37
37
  trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- trilogy/core/processing/concept_strategies_v3.py,sha256=dx6w7euQOozoNv-ugrFOHZcCSEkpG8yhK44QH5-yGt4,44046
38
+ trilogy/core/processing/concept_strategies_v3.py,sha256=tgLNXlwObWhcQmBGz8xpN4p-mFZ9Hl74VplQqDm86us,44105
39
39
  trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuXl-Te85lag,1205
40
40
  trilogy/core/processing/utility.py,sha256=rfzdgl-vWkCyhLzXNNuWgPLK59eiYypQb6TdZKymUqk,21469
41
41
  trilogy/core/processing/node_generators/__init__.py,sha256=o8rOFHPSo-s_59hREwXMW6gjUJCsiXumdbJNozHUf-Y,800
42
42
  trilogy/core/processing/node_generators/basic_node.py,sha256=UVsXMn6jTjm_ofVFt218jAS11s4RV4zD781vP4im-GI,3371
43
43
  trilogy/core/processing/node_generators/common.py,sha256=nVeH_AdO58ygtNSO0wNgMR7_h2D0dFSGM_rh1fJd4Yc,9468
44
- trilogy/core/processing/node_generators/filter_node.py,sha256=lT167yBgy3P9sDBM1Cjj0PKSXro8dvGtBmc8nwsUjig,8366
44
+ trilogy/core/processing/node_generators/filter_node.py,sha256=JymSKzA-9oQAZ3ZtJRK9c3w5FXs8MjJBGWU9TYUqx4E,9099
45
45
  trilogy/core/processing/node_generators/group_node.py,sha256=kO-ersxIL04rZwX5-vFIFQQnp357PFo_7ZKXoGq3wyc,5989
46
46
  trilogy/core/processing/node_generators/group_to_node.py,sha256=jKcNCDOY6fNblrdZwaRU0sbUSr9H0moQbAxrGgX6iGA,3832
47
47
  trilogy/core/processing/node_generators/multiselect_node.py,sha256=GWV5yLmKTe1yyPhN60RG1Rnrn4ktfn9lYYXi_FVU4UI,7061
@@ -55,7 +55,7 @@ trilogy/core/processing/node_generators/unnest_node.py,sha256=cOEKnMRzXUW3bwmiOl
55
55
  trilogy/core/processing/node_generators/window_node.py,sha256=RUHgpYovQObFod1xRIMWtDzMcxwlm4-1Fdrf_Cuw5W4,6346
56
56
  trilogy/core/processing/node_generators/select_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  trilogy/core/processing/node_generators/select_helpers/datasource_injection.py,sha256=GMW07bb6hXurhF0hZLYoMAKSIS65tat5hwBjvqqPeSA,6516
58
- trilogy/core/processing/nodes/__init__.py,sha256=Lxr3rs_bqOAtMtn3DHIkY058ZzjyLM5mSfGMKW2z0NY,5555
58
+ trilogy/core/processing/nodes/__init__.py,sha256=MfookoG73Z9p7kYFDh033uZJpP4exqkwdKtheDRepAw,5893
59
59
  trilogy/core/processing/nodes/base_node.py,sha256=FHrY8GsTKPuMJklOjILbhGqCt5s1nmlj62Z-molARDA,16835
60
60
  trilogy/core/processing/nodes/filter_node.py,sha256=5VtRfKbCORx0dV-vQfgy3gOEkmmscL9f31ExvlODwvY,2461
61
61
  trilogy/core/processing/nodes/group_node.py,sha256=MUvcOg9U5J6TnWBel8eht9PdI9BfAKjUxmfjP_ZXx9o,10484
@@ -102,8 +102,8 @@ trilogy/std/display.preql,sha256=2BbhvqR4rcltyAbOXAUo7SZ_yGFYZgFnurglHMbjW2g,40
102
102
  trilogy/std/geography.preql,sha256=-fqAGnBL6tR-UtT8DbSek3iMFg66ECR_B_41pODxv-k,504
103
103
  trilogy/std/money.preql,sha256=ZHW-csTX-kYbOLmKSO-TcGGgQ-_DMrUXy0BjfuJSFxM,80
104
104
  trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
105
- pytrilogy-0.0.3.44.dist-info/METADATA,sha256=-TUyQi3fENhsnYhqsy2HiD-thLyAz6S8TAmgdVuaCZo,9100
106
- pytrilogy-0.0.3.44.dist-info/WHEEL,sha256=GHB6lJx2juba1wDgXDNlMTyM13ckjBMKf-OnwgKOCtA,91
107
- pytrilogy-0.0.3.44.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
108
- pytrilogy-0.0.3.44.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
109
- pytrilogy-0.0.3.44.dist-info/RECORD,,
105
+ pytrilogy-0.0.3.46.dist-info/METADATA,sha256=1cBYEU3n78eaoNyqLsOS-eP_WylqceEDeFoktS7JzO0,9100
106
+ pytrilogy-0.0.3.46.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
107
+ pytrilogy-0.0.3.46.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
108
+ pytrilogy-0.0.3.46.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
109
+ pytrilogy-0.0.3.46.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.0)
2
+ Generator: setuptools (80.3.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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.44"
7
+ __version__ = "0.0.3.46"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -1906,7 +1906,7 @@ class AggregateWrapper(Mergeable, DataTyped, ConceptArgs, Namespaced, BaseModel)
1906
1906
 
1907
1907
 
1908
1908
  class FilterItem(DataTyped, Namespaced, ConceptArgs, BaseModel):
1909
- content: Expr
1909
+ content: FuncArgs
1910
1910
  where: "WhereClause"
1911
1911
 
1912
1912
  @field_validator("content", mode="before")
@@ -748,6 +748,7 @@ def search_concepts(
748
748
  accept_partial: bool = False,
749
749
  conditions: BuildWhereClause | None = None,
750
750
  ) -> StrategyNode | None:
751
+ logger.error(f"starting search for {mandatory_list}")
751
752
  hist = history.get_history(
752
753
  search=mandatory_list, accept_partial=accept_partial, conditions=conditions
753
754
  )
@@ -1094,6 +1095,7 @@ def _search_concepts(
1094
1095
  logger.error(
1095
1096
  f"{depth_to_prefix(depth)}{LOGGER_PREFIX} Could not resolve concepts {[c.address for c in mandatory_list]}, network outcome was {complete}, missing {all_mandatory - found},"
1096
1097
  )
1098
+
1097
1099
  return None
1098
1100
 
1099
1101
 
@@ -56,12 +56,40 @@ def gen_filter_node(
56
56
  continue
57
57
  if conditions and conditions == where:
58
58
  optional_included.append(x)
59
+
60
+ # sometimes, it's okay to include other local optional above the filter
61
+ # in case it is, prep our list
62
+ extra_row_level_optional: list[BuildConcept] = []
63
+ for x in local_optional:
64
+ if x.address in optional_included:
65
+ continue
66
+ extra_row_level_optional.append(x)
67
+
68
+ # this flag controls whether we materialize the filter as a where on the prior CTE
69
+ # or do the filtering inline as a case statement
70
+ optimized_pushdown = False
71
+ if not is_scalar_condition(where.conditional):
72
+ optimized_pushdown = False
73
+ elif not local_optional:
74
+ optimized_pushdown = True
75
+ elif conditions and conditions == where:
76
+ logger.info(
77
+ f"{padding(depth)}{LOGGER_PREFIX} query conditions are the same as filter conditions, can optimize across all concepts"
78
+ )
79
+ optimized_pushdown = True
80
+ elif optional_included == local_optional:
81
+ logger.info(
82
+ f"{padding(depth)}{LOGGER_PREFIX} all optional concepts are included in the filter, can optimize across all concepts"
83
+ )
84
+ optimized_pushdown = True
59
85
  logger.info(
60
86
  f"{padding(depth)}{LOGGER_PREFIX} filter `{concept}` condition `{concept.lineage.where}` derived from {immediate_parent.address} row parents {[x.address for x in parent_row_concepts]} and {[[y.address] for x in parent_existence_concepts for y in x]} existence parents"
61
87
  )
62
88
  # we'll populate this with the row parent
63
89
  # and the existence parent(s)
64
90
  core_parents = []
91
+ if not optimized_pushdown:
92
+ parent_row_concepts += extra_row_level_optional
65
93
 
66
94
  row_parent: StrategyNode = source_concepts(
67
95
  mandatory_list=parent_row_concepts,
@@ -99,21 +127,6 @@ def gen_filter_node(
99
127
  )
100
128
  return None
101
129
 
102
- optimized_pushdown = False
103
- if not is_scalar_condition(where.conditional):
104
- optimized_pushdown = False
105
- elif not local_optional:
106
- optimized_pushdown = True
107
- elif conditions and conditions == where:
108
- logger.info(
109
- f"{padding(depth)}{LOGGER_PREFIX} query conditions are the same as filter conditions, can optimize across all concepts"
110
- )
111
- optimized_pushdown = True
112
- elif optional_included == local_optional:
113
- logger.info(
114
- f"{padding(depth)}{LOGGER_PREFIX} all optional concepts are included in the filter, can optimize across all concepts"
115
- )
116
- optimized_pushdown = True
117
130
  if optimized_pushdown:
118
131
  logger.info(
119
132
  f"{padding(depth)}{LOGGER_PREFIX} returning optimized filter node with pushdown to parent with condition {where.conditional}"
@@ -193,7 +206,9 @@ def gen_filter_node(
193
206
  + optional_outputs
194
207
  )
195
208
  return filter_node
196
-
209
+ logger.info(
210
+ f"{padding(depth)}{LOGGER_PREFIX} need to enrich filter node with additional concepts {[x.address for x in local_optional if x.address not in filter_node.output_concepts]}"
211
+ )
197
212
  enrich_node: StrategyNode = source_concepts( # this fetches the parent + join keys
198
213
  # to then connect to the rest of the query
199
214
  mandatory_list=[immediate_parent] + parent_row_concepts + local_optional,
@@ -1,5 +1,6 @@
1
1
  from pydantic import BaseModel, ConfigDict, Field
2
2
 
3
+ from trilogy.core.exceptions import UnresolvableQueryException
3
4
  from trilogy.core.models.build import BuildConcept, BuildWhereClause
4
5
  from trilogy.core.models.build_environment import BuildEnvironment
5
6
  from trilogy.core.models.environment import Environment
@@ -18,7 +19,7 @@ class History(BaseModel):
18
19
  base_environment: Environment
19
20
  history: dict[str, StrategyNode | None] = Field(default_factory=dict)
20
21
  select_history: dict[str, StrategyNode | None] = Field(default_factory=dict)
21
- started: set[str] = Field(default_factory=set)
22
+ started: dict[str, int] = Field(default_factory=dict)
22
23
  model_config = ConfigDict(arbitrary_types_allowed=True)
23
24
 
24
25
  def _concepts_to_lookup(
@@ -27,13 +28,10 @@ class History(BaseModel):
27
28
  accept_partial: bool,
28
29
  conditions: BuildWhereClause | None = None,
29
30
  ) -> str:
31
+ base = sorted([c.address for c in search])
30
32
  if conditions:
31
- return (
32
- "-".join([c.address for c in search])
33
- + str(accept_partial)
34
- + str(conditions)
35
- )
36
- return "-".join([c.address for c in search]) + str(accept_partial)
33
+ return "-".join(base) + str(accept_partial) + str(conditions)
34
+ return "-".join(base) + str(accept_partial)
37
35
 
38
36
  def search_to_history(
39
37
  self,
@@ -80,13 +78,19 @@ class History(BaseModel):
80
78
  accept_partial: bool = False,
81
79
  conditions: BuildWhereClause | None = None,
82
80
  ):
83
- self.started.add(
84
- self._concepts_to_lookup(
85
- search,
86
- accept_partial=accept_partial,
87
- conditions=conditions,
88
- )
81
+ key = self._concepts_to_lookup(
82
+ search,
83
+ accept_partial=accept_partial,
84
+ conditions=conditions,
89
85
  )
86
+ if key in self.started:
87
+ self.started[key] += 1
88
+ else:
89
+ self.started[key] = 1
90
+ if self.started[key] > 5:
91
+ raise UnresolvableQueryException(
92
+ f"Was unable to resolve datasources to serve this query from model; unresolvable set was {search}. You may be querying unrelated concepts."
93
+ )
90
94
 
91
95
  def log_end(
92
96
  self,
@@ -94,13 +98,13 @@ class History(BaseModel):
94
98
  accept_partial: bool = False,
95
99
  conditions: BuildWhereClause | None = None,
96
100
  ):
97
- self.started.discard(
98
- self._concepts_to_lookup(
99
- search,
100
- accept_partial=accept_partial,
101
- conditions=conditions,
102
- )
101
+ key = self._concepts_to_lookup(
102
+ search,
103
+ accept_partial=accept_partial,
104
+ conditions=conditions,
103
105
  )
106
+ if key in self.started:
107
+ del self.started[key]
104
108
 
105
109
  def check_started(
106
110
  self,