pytrilogy 0.0.3.53__py3-none-any.whl → 0.0.3.54__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.53
3
+ Version: 0.0.3.54
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.53.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
- trilogy/__init__.py,sha256=4T41znLlekPuyFkWm5zr0OjLiQwfA1YBEpTjY1Xl-6s,303
1
+ pytrilogy-0.0.3.54.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
+ trilogy/__init__.py,sha256=Wz4e3LWZ4MWyBm3EuLrz5-n5-yyu96rHR7k-4JPA4RE,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
@@ -19,7 +19,7 @@ trilogy/core/exceptions.py,sha256=JPYyBcit3T_pRtlHdtKSeVJkIyWUTozW2aaut25A2xI,67
19
19
  trilogy/core/functions.py,sha256=IvqHyuO__o6Th8tkDWjb9cDxQDly6l3ZEfJ9y8YrTRU,29227
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=O7ag0IVQlJyWdAXBi_hHeU3Df5DRyd75Vlz6pks2J10,8197
22
+ trilogy/core/optimization.py,sha256=ClDPMuuLBjuZCHdRGsuNhKNNTkbev1I22SbnLMzHT60,8839
23
23
  trilogy/core/query_processor.py,sha256=NNzYPKN5HzivQFXugSbJC_MaupkwOYii7A_vnXuBIK4,20063
24
24
  trilogy/core/utility.py,sha256=3VC13uSQWcZNghgt7Ot0ZTeEmNqs__cx122abVq9qhM,410
25
25
  trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -46,17 +46,17 @@ trilogy/core/processing/node_generators/group_node.py,sha256=nIfiMrJQEksUfqAeeA3
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
48
48
  trilogy/core/processing/node_generators/node_merge_node.py,sha256=sv55oynfqgpHEpo1OEtVDri-5fywzPhDlR85qaWikvY,16195
49
- trilogy/core/processing/node_generators/rowset_node.py,sha256=YmBs6ZQ7azLXRFEmeoecpGjK4pMHsUCovuBxfb3UKZI,6848
49
+ trilogy/core/processing/node_generators/rowset_node.py,sha256=2BiSsegbRF9csJ_Xl8P_CxIm4dAAb7dF29u6v_Odr-A,6709
50
50
  trilogy/core/processing/node_generators/select_merge_node.py,sha256=lxXhMhDKGbu67QFNbbAT-BO8gbWppIvjn_hAXpLEPe0,19953
51
51
  trilogy/core/processing/node_generators/select_node.py,sha256=Y-zO0AFkTrpi2LyebjpyHU7WWANr7nKZSS9rY7DH4Wo,1888
52
52
  trilogy/core/processing/node_generators/synonym_node.py,sha256=9LHK2XHDjbyTLjmDQieskG8fqbiSpRnFOkfrutDnOTE,2258
53
53
  trilogy/core/processing/node_generators/union_node.py,sha256=VNo6Oey4p8etU9xrOh2oTT2lIOTvY6PULUPRvVa2uxU,2877
54
54
  trilogy/core/processing/node_generators/unnest_node.py,sha256=cOEKnMRzXUW3bwmiOlgn3E1-B38osng0dh2pDykwITY,2410
55
- trilogy/core/processing/node_generators/window_node.py,sha256=RUHgpYovQObFod1xRIMWtDzMcxwlm4-1Fdrf_Cuw5W4,6346
55
+ trilogy/core/processing/node_generators/window_node.py,sha256=GP3Hvkbb0TDA6ef7W7bmvQEHVH-NRIfBT_0W4fcH3g4,6529
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=xPFF7x3TFs1Z4IcfthCykZgrksb-UhN-pc_oIigfFSo,6014
59
- trilogy/core/processing/nodes/base_node.py,sha256=z-aZEVjnLdFm6TpmneEm2bnRXj-tRFr7mN7DYG4zH9A,16967
58
+ trilogy/core/processing/nodes/__init__.py,sha256=oHDknqAZFm6KSDDbzJXj8HDGDh-dn-YjHDGqiXc8Psw,6054
59
+ trilogy/core/processing/nodes/base_node.py,sha256=IdKR2yaQGY1iRgKXgxF1UtlyuJEmPXWRh0rGFXv7Z_U,18111
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
62
62
  trilogy/core/processing/nodes/merge_node.py,sha256=02oWRca0ba41U6PSAB14jwnWWxoyrvxRPLwkli259SY,15865
@@ -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.53.dist-info/METADATA,sha256=AXRI2M3hD9xHGN8YuQ_nxnylPcy6zSWmKS3cBjZjtek,9095
106
- pytrilogy-0.0.3.53.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
107
- pytrilogy-0.0.3.53.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
108
- pytrilogy-0.0.3.53.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
109
- pytrilogy-0.0.3.53.dist-info/RECORD,,
105
+ pytrilogy-0.0.3.54.dist-info/METADATA,sha256=_Bv_Q8dZSrH750iNkW7KtpUP1vSAwZRnBzsG8KDXdXE,9095
106
+ pytrilogy-0.0.3.54.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
107
+ pytrilogy-0.0.3.54.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
108
+ pytrilogy-0.0.3.54.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
109
+ pytrilogy-0.0.3.54.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.8.0)
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.53"
7
+ __version__ = "0.0.3.54"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -120,6 +120,13 @@ def gen_inverse_map(input: list[CTE | UnionCTE]) -> dict[str, list[CTE | UnionCT
120
120
  return inverse_map
121
121
 
122
122
 
123
+ SENSITIVE_DERIVATIONS = [
124
+ Derivation.UNNEST,
125
+ Derivation.WINDOW,
126
+ # Derivation.AGGREGATE,
127
+ ]
128
+
129
+
123
130
  def is_direct_return_eligible(cte: CTE | UnionCTE) -> CTE | UnionCTE | None:
124
131
  # if isinstance(select, (PersistStatement, MultiSelectStatement)):
125
132
  # return False
@@ -151,21 +158,31 @@ def is_direct_return_eligible(cte: CTE | UnionCTE) -> CTE | UnionCTE | None:
151
158
  ]
152
159
  condition_arguments = cte.condition.row_arguments if cte.condition else []
153
160
  for x in derived_concepts:
154
- if x.derivation == Derivation.WINDOW:
155
- return None
156
- if x.derivation == Derivation.UNNEST:
157
- return None
158
- if x.derivation == Derivation.AGGREGATE:
161
+ if x.derivation in SENSITIVE_DERIVATIONS:
159
162
  return None
160
163
  for x in parent_derived_concepts:
161
164
  if x.address not in condition_arguments:
162
165
  continue
163
- if x.derivation == Derivation.UNNEST:
164
- return None
165
- if x.derivation == Derivation.WINDOW:
166
+ if x.derivation in SENSITIVE_DERIVATIONS:
166
167
  return None
168
+ for x in condition_arguments:
169
+ # if it's derived in the parent
170
+ if x.address in parent_derived_concepts:
171
+ if x.derivation in SENSITIVE_DERIVATIONS:
172
+ return None
173
+ # this maybe needs to be recursive if we flatten a ton of derivation
174
+ # into one CTE
175
+ if not x.lineage:
176
+ continue
177
+ for z in x.lineage.concept_arguments:
178
+ # if it was preexisting in the parent, it's safe
179
+ if z.address in direct_parent.source.input_concepts:
180
+ continue
181
+ # otherwise if it's dangerous, play it safe.
182
+ if z.derivation in SENSITIVE_DERIVATIONS:
183
+ return None
167
184
  logger.info(
168
- f"[Optimization][EarlyReturn] Removing redundant output CTE with derived_concepts {[x.address for x in derived_concepts]}"
185
+ f"[Optimization][EarlyReturn] Removing redundant output CTE {cte.name} with derived_concepts {[x.address for x in derived_concepts]}"
169
186
  )
170
187
  return direct_parent
171
188
 
@@ -95,9 +95,7 @@ def gen_rowset_node(
95
95
  f"{padding(depth)}{LOGGER_PREFIX} hiding {final_hidden} local optional {local_optional}"
96
96
  )
97
97
  node.hide_output_concepts(final_hidden)
98
- assert node.resolution_cache
99
- # assume grain to be output of select
100
- # but don't include anything hidden(the non-rowset concepts)
98
+
101
99
  node.grain = BuildGrain.from_concepts(
102
100
  [
103
101
  x
@@ -11,7 +11,12 @@ from trilogy.core.models.build_environment import BuildEnvironment
11
11
  from trilogy.core.processing.node_generators.common import (
12
12
  gen_enrichment_node,
13
13
  )
14
- from trilogy.core.processing.nodes import History, StrategyNode, WindowNode
14
+ from trilogy.core.processing.nodes import (
15
+ History,
16
+ StrategyNode,
17
+ WhereSafetyNode,
18
+ WindowNode,
19
+ )
15
20
  from trilogy.core.processing.utility import create_log_lambda, padding
16
21
  from trilogy.utility import unique
17
22
 
@@ -71,10 +76,13 @@ def gen_window_node(
71
76
  if equivalent_optional:
72
77
  for x in equivalent_optional:
73
78
  assert isinstance(x.lineage, WINDOW_TYPES)
79
+ base, parents = resolve_window_parent_concepts(x, environment)
74
80
  logger.info(
75
- f"{padding(depth)}{LOGGER_PREFIX} found equivalent optional {x} with parents {resolve_window_parent_concepts(x, environment)[1]}"
81
+ f"{padding(depth)}{LOGGER_PREFIX} found equivalent optional {x} with parents {parents}"
76
82
  )
77
83
  additional_outputs.append(x)
84
+ # also append the base concept it's being grouped over
85
+ targets.append(base)
78
86
 
79
87
  grain_equivalents = [
80
88
  x
@@ -85,7 +93,8 @@ def gen_window_node(
85
93
  ]
86
94
 
87
95
  for x in grain_equivalents:
88
- logger.info("Appending grain equivalent %s", x)
96
+ if x.address in additional_outputs:
97
+ continue
89
98
  targets.append(x)
90
99
 
91
100
  # finally, the ones we'll need to enrich
@@ -134,7 +143,7 @@ def gen_window_node(
134
143
  _window_node.rebuild_cache()
135
144
  _window_node.resolve()
136
145
 
137
- window_node = StrategyNode(
146
+ window_node = WhereSafetyNode(
138
147
  input_concepts=[concept] + additional_outputs + parent_concepts + targets,
139
148
  output_concepts=[concept] + additional_outputs + parent_concepts + targets,
140
149
  environment=environment,
@@ -6,7 +6,7 @@ from trilogy.core.models.build import BuildConcept, BuildWhereClause
6
6
  from trilogy.core.models.build_environment import BuildEnvironment
7
7
  from trilogy.core.models.environment import Environment
8
8
 
9
- from .base_node import NodeJoin, StrategyNode
9
+ from .base_node import NodeJoin, StrategyNode, WhereSafetyNode
10
10
  from .filter_node import FilterNode
11
11
  from .group_node import GroupNode
12
12
  from .merge_node import MergeNode
@@ -193,4 +193,5 @@ __all__ = [
193
193
  "UnnestNode",
194
194
  "UnionNode",
195
195
  "History",
196
+ "WhereSafetyNode",
196
197
  ]
@@ -291,9 +291,14 @@ class StrategyNode:
291
291
  def add_output_concept(self, concept: BuildConcept, rebuild: bool = True):
292
292
  return self.add_output_concepts([concept], rebuild)
293
293
 
294
- def hide_output_concepts(self, concepts: List[BuildConcept], rebuild: bool = True):
294
+ def hide_output_concepts(
295
+ self, concepts: List[BuildConcept] | list[str] | set[str], rebuild: bool = True
296
+ ):
295
297
  for x in concepts:
296
- self.hidden_concepts.add(x.address)
298
+ if isinstance(x, BuildConcept):
299
+ self.hidden_concepts.add(x.address)
300
+ else:
301
+ self.hidden_concepts.add(x)
297
302
  if rebuild:
298
303
  self.rebuild_cache()
299
304
  return self
@@ -471,3 +476,28 @@ class NodeJoin:
471
476
  f" {self.right_node} on"
472
477
  f" {','.join([str(k) for k in self.concepts])}"
473
478
  )
479
+
480
+
481
+ class WhereSafetyNode(StrategyNode):
482
+ """Specialized node to be used to pad certain
483
+ select outputs that can't be immediately used in a where
484
+ clause; eg window functions. Will remove itself if not required."""
485
+
486
+ def resolve(self) -> QueryDatasource:
487
+ if not self.conditions and len(self.parents) == 1:
488
+ parent = self.parents[0]
489
+ parent = parent.copy()
490
+ # avoid performance hit by not rebuilding until end
491
+ parent.set_output_concepts(self.output_concepts, rebuild=False)
492
+ parent.hide_output_concepts(self.hidden_concepts, rebuild=False)
493
+
494
+ # these conditions
495
+ if self.preexisting_conditions:
496
+ parent.set_preexisting_conditions(self.preexisting_conditions)
497
+ # TODO: add a helper for this
498
+ parent.ordering = self.ordering
499
+
500
+ # actually build the node
501
+ parent.rebuild_cache()
502
+ return parent.resolve()
503
+ return super().resolve()