pytrilogy 0.0.3.53__tar.gz → 0.0.3.54__tar.gz

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.

Files changed (141) hide show
  1. {pytrilogy-0.0.3.53/pytrilogy.egg-info → pytrilogy-0.0.3.54}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/__init__.py +1 -1
  4. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/optimization.py +26 -9
  5. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/rowset_node.py +1 -3
  6. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/window_node.py +13 -4
  7. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/nodes/__init__.py +2 -1
  8. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/nodes/base_node.py +32 -2
  9. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/LICENSE.md +0 -0
  10. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/README.md +0 -0
  11. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/pyproject.toml +0 -0
  12. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/pytrilogy.egg-info/SOURCES.txt +0 -0
  13. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/pytrilogy.egg-info/dependency_links.txt +0 -0
  14. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/pytrilogy.egg-info/entry_points.txt +0 -0
  15. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/pytrilogy.egg-info/requires.txt +0 -0
  16. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/pytrilogy.egg-info/top_level.txt +0 -0
  17. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/setup.cfg +0 -0
  18. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/setup.py +0 -0
  19. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_datatypes.py +0 -0
  20. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_declarations.py +0 -0
  21. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_derived_concepts.py +0 -0
  22. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_discovery_nodes.py +0 -0
  23. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_enums.py +0 -0
  24. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_environment.py +0 -0
  25. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_executor.py +0 -0
  26. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_failure.py +0 -0
  27. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_functions.py +0 -0
  28. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_imports.py +0 -0
  29. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_metadata.py +0 -0
  30. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_models.py +0 -0
  31. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_multi_join_assignments.py +0 -0
  32. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_parse_engine.py +0 -0
  33. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_parsing.py +0 -0
  34. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_parsing_failures.py +0 -0
  35. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_partial_handling.py +0 -0
  36. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_query_processing.py +0 -0
  37. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_query_render.py +0 -0
  38. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_select.py +0 -0
  39. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_show.py +0 -0
  40. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_statements.py +0 -0
  41. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_typing.py +0 -0
  42. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_undefined_concept.py +0 -0
  43. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_user_functions.py +0 -0
  44. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/tests/test_where_clause.py +0 -0
  45. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/authoring/__init__.py +0 -0
  46. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/compiler.py +0 -0
  47. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/constants.py +0 -0
  48. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/__init__.py +0 -0
  49. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/constants.py +0 -0
  50. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/enums.py +0 -0
  51. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/env_processor.py +0 -0
  52. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/environment_helpers.py +0 -0
  53. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/ergonomics.py +0 -0
  54. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/exceptions.py +0 -0
  55. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/functions.py +0 -0
  56. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/graph_models.py +0 -0
  57. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/internal.py +0 -0
  58. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/models/__init__.py +0 -0
  59. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/models/author.py +0 -0
  60. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/models/build.py +0 -0
  61. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/models/build_environment.py +0 -0
  62. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/models/core.py +0 -0
  63. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/models/datasource.py +0 -0
  64. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/models/environment.py +0 -0
  65. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/models/execute.py +0 -0
  66. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/optimizations/__init__.py +0 -0
  67. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/optimizations/base_optimization.py +0 -0
  68. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/optimizations/inline_datasource.py +0 -0
  69. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  70. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/__init__.py +0 -0
  71. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  72. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/graph_utils.py +0 -0
  73. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/__init__.py +0 -0
  74. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  75. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/common.py +0 -0
  76. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  77. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/group_node.py +0 -0
  78. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  79. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  80. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  81. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  82. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  83. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  84. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/select_node.py +0 -0
  85. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  86. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/union_node.py +0 -0
  87. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  88. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/nodes/filter_node.py +0 -0
  89. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/nodes/group_node.py +0 -0
  90. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/nodes/merge_node.py +0 -0
  91. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  92. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/nodes/union_node.py +0 -0
  93. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  94. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/nodes/window_node.py +0 -0
  95. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/processing/utility.py +0 -0
  96. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/query_processor.py +0 -0
  97. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/statements/__init__.py +0 -0
  98. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/statements/author.py +0 -0
  99. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/statements/build.py +0 -0
  100. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/statements/common.py +0 -0
  101. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/statements/execute.py +0 -0
  102. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/core/utility.py +0 -0
  103. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/__init__.py +0 -0
  104. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/base.py +0 -0
  105. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/bigquery.py +0 -0
  106. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/common.py +0 -0
  107. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/config.py +0 -0
  108. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/dataframe.py +0 -0
  109. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/duckdb.py +0 -0
  110. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/enums.py +0 -0
  111. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/postgres.py +0 -0
  112. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/presto.py +0 -0
  113. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/snowflake.py +0 -0
  114. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/dialect/sql_server.py +0 -0
  115. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/engine.py +0 -0
  116. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/executor.py +0 -0
  117. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/hooks/__init__.py +0 -0
  118. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/hooks/base_hook.py +0 -0
  119. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/hooks/graph_hook.py +0 -0
  120. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/hooks/query_debugger.py +0 -0
  121. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/metadata/__init__.py +0 -0
  122. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/parser.py +0 -0
  123. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/parsing/__init__.py +0 -0
  124. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/parsing/common.py +0 -0
  125. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/parsing/config.py +0 -0
  126. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/parsing/exceptions.py +0 -0
  127. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/parsing/helpers.py +0 -0
  128. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/parsing/parse_engine.py +0 -0
  129. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/parsing/render.py +0 -0
  130. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/parsing/trilogy.lark +0 -0
  131. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/py.typed +0 -0
  132. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/render.py +0 -0
  133. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/scripts/__init__.py +0 -0
  134. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/scripts/trilogy.py +0 -0
  135. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/std/__init__.py +0 -0
  136. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/std/date.preql +0 -0
  137. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/std/display.preql +0 -0
  138. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/std/geography.preql +0 -0
  139. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/std/money.preql +0 -0
  140. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/std/report.preql +0 -0
  141. {pytrilogy-0.0.3.53 → pytrilogy-0.0.3.54}/trilogy/utility.py +0 -0
@@ -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,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:
@@ -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()
File without changes
File without changes
File without changes
File without changes