pytrilogy 0.0.2.9__tar.gz → 0.0.2.10__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 (105) hide show
  1. {pytrilogy-0.0.2.9/pytrilogy.egg-info → pytrilogy-0.0.2.10}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/__init__.py +1 -1
  4. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/constants.py +1 -1
  5. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/models.py +17 -5
  6. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/filter_node.py +7 -2
  7. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/nodes/group_node.py +1 -5
  8. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/utility.py +18 -5
  9. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/query_processor.py +1 -1
  10. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/dialect/base.py +17 -11
  11. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/dialect/duckdb.py +1 -1
  12. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/parsing/parse_engine.py +19 -1
  13. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/LICENSE.md +0 -0
  14. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/README.md +0 -0
  15. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/pyproject.toml +0 -0
  16. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/pytrilogy.egg-info/SOURCES.txt +0 -0
  17. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/pytrilogy.egg-info/dependency_links.txt +0 -0
  18. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/pytrilogy.egg-info/entry_points.txt +0 -0
  19. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/pytrilogy.egg-info/requires.txt +0 -0
  20. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/pytrilogy.egg-info/top_level.txt +0 -0
  21. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/setup.cfg +0 -0
  22. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/setup.py +0 -0
  23. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_datatypes.py +0 -0
  24. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_declarations.py +0 -0
  25. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_derived_concepts.py +0 -0
  26. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_discovery_nodes.py +0 -0
  27. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_environment.py +0 -0
  28. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_functions.py +0 -0
  29. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_imports.py +0 -0
  30. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_metadata.py +0 -0
  31. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_models.py +0 -0
  32. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_multi_join_assignments.py +0 -0
  33. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_parsing.py +0 -0
  34. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_partial_handling.py +0 -0
  35. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_query_processing.py +0 -0
  36. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_select.py +0 -0
  37. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_statements.py +0 -0
  38. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_undefined_concept.py +0 -0
  39. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/tests/test_where_clause.py +0 -0
  40. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/compiler.py +0 -0
  41. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/__init__.py +0 -0
  42. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/constants.py +0 -0
  43. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/enums.py +0 -0
  44. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/env_processor.py +0 -0
  45. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/environment_helpers.py +0 -0
  46. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/ergonomics.py +0 -0
  47. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/exceptions.py +0 -0
  48. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/functions.py +0 -0
  49. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/graph_models.py +0 -0
  50. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/internal.py +0 -0
  51. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/optimization.py +0 -0
  52. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/optimizations/__init__.py +0 -0
  53. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/optimizations/base_optimization.py +0 -0
  54. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/optimizations/inline_constant.py +0 -0
  55. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/optimizations/inline_datasource.py +0 -0
  56. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  57. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/__init__.py +0 -0
  58. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  59. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/graph_utils.py +0 -0
  60. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/__init__.py +0 -0
  61. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  62. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/common.py +0 -0
  63. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/group_node.py +0 -0
  64. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  65. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  66. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  67. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  68. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/select_node.py +0 -0
  69. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  70. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/node_generators/window_node.py +0 -0
  71. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/nodes/__init__.py +0 -0
  72. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/nodes/base_node.py +0 -0
  73. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/nodes/filter_node.py +0 -0
  74. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/nodes/merge_node.py +0 -0
  75. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  76. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  77. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/core/processing/nodes/window_node.py +0 -0
  78. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/dialect/__init__.py +0 -0
  79. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/dialect/bigquery.py +0 -0
  80. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/dialect/common.py +0 -0
  81. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/dialect/config.py +0 -0
  82. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/dialect/enums.py +0 -0
  83. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/dialect/postgres.py +0 -0
  84. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/dialect/presto.py +0 -0
  85. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/dialect/snowflake.py +0 -0
  86. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/dialect/sql_server.py +0 -0
  87. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/engine.py +0 -0
  88. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/executor.py +0 -0
  89. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/hooks/__init__.py +0 -0
  90. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/hooks/base_hook.py +0 -0
  91. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/hooks/graph_hook.py +0 -0
  92. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/hooks/query_debugger.py +0 -0
  93. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/metadata/__init__.py +0 -0
  94. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/parser.py +0 -0
  95. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/parsing/__init__.py +0 -0
  96. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/parsing/common.py +0 -0
  97. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/parsing/config.py +0 -0
  98. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/parsing/exceptions.py +0 -0
  99. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/parsing/helpers.py +0 -0
  100. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/parsing/render.py +0 -0
  101. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/parsing/trilogy.lark +0 -0
  102. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/py.typed +0 -0
  103. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/scripts/__init__.py +0 -0
  104. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/scripts/trilogy.py +0 -0
  105. {pytrilogy-0.0.2.9 → pytrilogy-0.0.2.10}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.9
3
+ Version: 0.0.2.10
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.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.9
3
+ Version: 0.0.2.10
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -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.9"
7
+ __version__ = "0.0.2.10"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -24,7 +24,7 @@ class Optimizations:
24
24
  predicate_pushdown: bool = True
25
25
  datasource_inlining: bool = True
26
26
  constant_inlining: bool = True
27
- constant_inline_cutoff: int = 2
27
+ constant_inline_cutoff: int = 3
28
28
  direct_return: bool = True
29
29
 
30
30
 
@@ -70,7 +70,7 @@ from trilogy.utility import unique
70
70
  from collections import UserList, UserDict
71
71
  from functools import cached_property
72
72
  from abc import ABC
73
-
73
+ from collections import defaultdict
74
74
 
75
75
  LOGGER_PREFIX = "[MODELS]"
76
76
 
@@ -2027,7 +2027,7 @@ class Datasource(Namespaced, BaseModel):
2027
2027
  return self.__repr__()
2028
2028
 
2029
2029
  def __hash__(self):
2030
- return (self.namespace + self.identifier).__hash__()
2030
+ return self.full_name.__hash__()
2031
2031
 
2032
2032
  def with_namespace(self, namespace: str):
2033
2033
  new_namespace = (
@@ -2212,9 +2212,9 @@ class BaseJoin(BaseModel):
2212
2212
  class QueryDatasource(BaseModel):
2213
2213
  input_concepts: List[Concept]
2214
2214
  output_concepts: List[Concept]
2215
+ datasources: List[Union[Datasource, "QueryDatasource"]]
2215
2216
  source_map: Dict[str, Set[Union[Datasource, "QueryDatasource", "UnnestJoin"]]]
2216
2217
 
2217
- datasources: List[Union[Datasource, "QueryDatasource"]]
2218
2218
  grain: Grain
2219
2219
  joins: List[BaseJoin | UnnestJoin]
2220
2220
  limit: Optional[int] = None
@@ -2266,7 +2266,7 @@ class QueryDatasource(BaseModel):
2266
2266
 
2267
2267
  @field_validator("source_map")
2268
2268
  @classmethod
2269
- def validate_source_map(cls, v, info: ValidationInfo):
2269
+ def validate_source_map(cls, v: dict, info: ValidationInfo):
2270
2270
  values = info.data
2271
2271
  for key in ("input_concepts", "output_concepts"):
2272
2272
  if not values.get(key):
@@ -2344,11 +2344,23 @@ class QueryDatasource(BaseModel):
2344
2344
  )
2345
2345
 
2346
2346
  merged_datasources = {}
2347
+
2347
2348
  for ds in [*self.datasources, *other.datasources]:
2348
2349
  if ds.full_name in merged_datasources:
2349
2350
  merged_datasources[ds.full_name] = merged_datasources[ds.full_name] + ds
2350
2351
  else:
2351
2352
  merged_datasources[ds.full_name] = ds
2353
+
2354
+ final_source_map = defaultdict(set)
2355
+ for key in self.source_map:
2356
+ final_source_map[key] = self.source_map[key].union(
2357
+ other.source_map.get(key, set())
2358
+ )
2359
+ for key in other.source_map:
2360
+ if key not in final_source_map:
2361
+ final_source_map[key] = other.source_map[key]
2362
+ for k, v in final_source_map.items():
2363
+ final_source_map[k] = set(merged_datasources[x.full_name] for x in list(v))
2352
2364
  qds = QueryDatasource(
2353
2365
  input_concepts=unique(
2354
2366
  self.input_concepts + other.input_concepts, "address"
@@ -2356,7 +2368,7 @@ class QueryDatasource(BaseModel):
2356
2368
  output_concepts=unique(
2357
2369
  self.output_concepts + other.output_concepts, "address"
2358
2370
  ),
2359
- source_map={**self.source_map, **other.source_map},
2371
+ source_map=final_source_map,
2360
2372
  datasources=list(merged_datasources.values()),
2361
2373
  grain=self.grain,
2362
2374
  joins=unique(self.joins + other.joins, "unique_id"),
@@ -17,6 +17,7 @@ from trilogy.core.processing.node_generators.common import (
17
17
  from trilogy.constants import logger
18
18
  from trilogy.core.processing.utility import padding, unique
19
19
  from trilogy.core.processing.node_generators.common import concept_to_relevant_joins
20
+ from trilogy.core.processing.utility import is_scalar_condition
20
21
 
21
22
  LOGGER_PREFIX = "[GEN_FILTER_NODE]"
22
23
 
@@ -78,16 +79,20 @@ def gen_filter_node(
78
79
  return None
79
80
 
80
81
  optimized_pushdown = False
81
- if not local_optional:
82
+ if not is_scalar_condition(where.conditional):
83
+ optimized_pushdown = False
84
+ elif not local_optional:
82
85
  optimized_pushdown = True
83
86
  elif conditions and conditions == where:
84
87
  logger.info(
85
88
  f"{padding(depth)}{LOGGER_PREFIX} query conditions are the same as filter conditions, can optimize across all concepts"
86
89
  )
87
90
  optimized_pushdown = True
88
-
89
91
  if optimized_pushdown:
90
92
  if isinstance(row_parent, SelectNode):
93
+ logger.info(
94
+ f"{padding(depth)}{LOGGER_PREFIX} nesting select node in strategy node"
95
+ )
91
96
  parent = StrategyNode(
92
97
  input_concepts=row_parent.output_concepts,
93
98
  output_concepts=[concept] + row_parent.output_concepts,
@@ -130,7 +130,7 @@ class GroupNode(StrategyNode):
130
130
  if self.conditions
131
131
  else self.output_concepts
132
132
  ),
133
- inherited_inputs=self.input_concepts,
133
+ inherited_inputs=self.input_concepts + self.existence_concepts,
134
134
  ),
135
135
  joins=[],
136
136
  grain=grain,
@@ -139,10 +139,6 @@ class GroupNode(StrategyNode):
139
139
  )
140
140
  # if there is a condition on a group node and it's not scalar
141
141
  # inject an additional CTE
142
- if self.conditions:
143
- logger.info("CONDITIONS")
144
- logger.info(str(self.conditions))
145
- logger.info(is_scalar_condition(self.conditions))
146
142
  if self.conditions and not is_scalar_condition(self.conditions):
147
143
  base.condition = None
148
144
  base.output_concepts = self.output_concepts + self.conditions.row_arguments
@@ -355,27 +355,40 @@ def is_scalar_condition(
355
355
  | MagicConstants
356
356
  | DataType
357
357
  ),
358
+ materialized: set[str] | None = None,
358
359
  ) -> bool:
359
360
  if isinstance(element, Parenthetical):
360
- return is_scalar_condition(element.content)
361
+ return is_scalar_condition(element.content, materialized)
361
362
  elif isinstance(element, SubselectComparison):
362
363
  return True
363
364
  elif isinstance(element, Comparison):
364
- return is_scalar_condition(element.left) and is_scalar_condition(element.right)
365
+ return is_scalar_condition(element.left, materialized) and is_scalar_condition(
366
+ element.right, materialized
367
+ )
365
368
  elif isinstance(element, Function):
366
369
  if element.operator in FunctionClass.AGGREGATE_FUNCTIONS.value:
367
370
  return False
371
+ elif isinstance(element, Concept):
372
+ if materialized and element.address in materialized:
373
+ return True
374
+ if element.lineage and isinstance(element.lineage, AggregateWrapper):
375
+ return is_scalar_condition(element.lineage, materialized)
376
+ return True
368
377
  elif isinstance(element, AggregateWrapper):
369
- return is_scalar_condition(element.function)
378
+ return is_scalar_condition(element.function, materialized)
370
379
  elif isinstance(element, Conditional):
371
- return is_scalar_condition(element.left) and is_scalar_condition(element.right)
380
+ return is_scalar_condition(element.left, materialized) and is_scalar_condition(
381
+ element.right, materialized
382
+ )
372
383
  return True
373
384
 
374
385
 
375
386
  def decompose_condition(
376
- conditional: Conditional,
387
+ conditional: Conditional | Comparison | Parenthetical,
377
388
  ) -> list[SubselectComparison | Comparison | Conditional | Parenthetical]:
378
389
  chunks: list[SubselectComparison | Comparison | Conditional | Parenthetical] = []
390
+ if not isinstance(conditional, Conditional):
391
+ return [conditional]
379
392
  if conditional.operator == BooleanOperator.AND:
380
393
  if not (
381
394
  isinstance(
@@ -261,7 +261,7 @@ def datasource_to_ctes(
261
261
 
262
262
  human_id = generate_cte_name(query_datasource.full_name, name_map)
263
263
  logger.info(
264
- f"Finished building source map for {human_id} with {len(parents)} parents, have {source_map}, query_datasource had non-empty keys {[k for k, v in query_datasource.source_map.items() if v]} "
264
+ f"Finished building source map for {human_id} with {len(parents)} parents, have {source_map}, query_datasource had non-empty keys {[k for k, v in query_datasource.source_map.items() if v]} and existence had non-empty keys {[k for k, v in query_datasource.existence_source_map.items() if v]} "
265
265
  )
266
266
  final_joins = [
267
267
  x
@@ -2,7 +2,7 @@ from typing import List, Union, Optional, Dict, Any, Sequence, Callable
2
2
 
3
3
  from jinja2 import Template
4
4
 
5
- from trilogy.core.processing.utility import is_scalar_condition
5
+ from trilogy.core.processing.utility import is_scalar_condition, decompose_condition
6
6
  from trilogy.constants import CONFIG, logger, MagicConstants
7
7
  from trilogy.core.internal import DEFAULT_CONCEPTS
8
8
  from trilogy.core.enums import (
@@ -538,6 +538,20 @@ class BaseDialect:
538
538
  final_joins = []
539
539
  else:
540
540
  final_joins = cte.joins or []
541
+ where: Conditional | Parenthetical | Comparison | None = None
542
+ having: Conditional | Parenthetical | Comparison | None = None
543
+ materialized = {x for x, v in cte.source_map.items() if v}
544
+ if cte.condition:
545
+ if is_scalar_condition(cte.condition, materialized=materialized):
546
+ where = cte.condition
547
+ else:
548
+ components = decompose_condition(cte.condition)
549
+ for x in components:
550
+ if is_scalar_condition(x, materialized=materialized):
551
+ where = where + x if where else x
552
+ else:
553
+ having = having + x if having else x
554
+
541
555
  return CompiledCTE(
542
556
  name=cte.name,
543
557
  statement=self.SQL_TEMPLATE.render(
@@ -561,16 +575,8 @@ class BaseDialect:
561
575
  ]
562
576
  if j
563
577
  ],
564
- where=(
565
- self.render_expr(cte.condition, cte)
566
- if cte.condition and is_scalar_condition(cte.condition)
567
- else None
568
- ),
569
- having=(
570
- self.render_expr(cte.condition, cte)
571
- if cte.condition and not is_scalar_condition(cte.condition)
572
- else None
573
- ),
578
+ where=(self.render_expr(where, cte) if where else None),
579
+ having=(self.render_expr(having, cte) if having else None),
574
580
  order_by=(
575
581
  [self.render_order_item(i, cte) for i in cte.order_by.items]
576
582
  if cte.order_by
@@ -36,7 +36,7 @@ FUNCTION_MAP = {
36
36
  # we may return a static value
37
37
  FUNCTION_GRAIN_MATCH_MAP = {
38
38
  **FUNCTION_MAP,
39
- FunctionType.COUNT: lambda args: "1",
39
+ FunctionType.COUNT: lambda args: f"{args[0]}",
40
40
  FunctionType.SUM: lambda args: f"{args[0]}",
41
41
  FunctionType.AVG: lambda args: f"{args[0]}",
42
42
  }
@@ -1116,7 +1116,25 @@ class ParseToObjects(Transformer):
1116
1116
  def comparison(self, args) -> Comparison:
1117
1117
  if args[1] == ComparisonOperator.IN:
1118
1118
  raise SyntaxError
1119
- return Comparison(left=args[0], right=args[2], operator=args[1])
1119
+ if isinstance(args[0], AggregateWrapper):
1120
+ left = arbitrary_to_concept(
1121
+ args[0],
1122
+ namespace=self.environment.namespace,
1123
+ name=f"{VIRTUAL_CONCEPT_PREFIX}_{string_to_hash(str(args[0]))}",
1124
+ )
1125
+ self.environment.add_concept(left)
1126
+ else:
1127
+ left = args[0]
1128
+ if isinstance(args[2], AggregateWrapper):
1129
+ right = arbitrary_to_concept(
1130
+ args[2],
1131
+ namespace=self.environment.namespace,
1132
+ name=f"{VIRTUAL_CONCEPT_PREFIX}_{string_to_hash(str(args[2]))}",
1133
+ )
1134
+ self.environment.add_concept(right)
1135
+ else:
1136
+ right = args[2]
1137
+ return Comparison(left=left, right=right, operator=args[1])
1120
1138
 
1121
1139
  def between_comparison(self, args) -> Conditional:
1122
1140
  left_bound = args[1]
File without changes
File without changes
File without changes
File without changes
File without changes