pytrilogy 0.0.2.40__tar.gz → 0.0.2.42__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 (110) hide show
  1. {pytrilogy-0.0.2.40/pytrilogy.egg-info → pytrilogy-0.0.2.42}/PKG-INFO +2 -2
  2. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/README.md +1 -1
  3. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42/pytrilogy.egg-info}/PKG-INFO +2 -2
  4. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_functions.py +10 -0
  5. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_select.py +4 -4
  6. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_show.py +2 -2
  7. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_statements.py +2 -2
  8. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/__init__.py +1 -1
  9. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/models.py +6 -0
  10. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/optimizations/predicate_pushdown.py +11 -2
  11. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/basic_node.py +30 -2
  12. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/window_node.py +30 -7
  13. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/dialect/base.py +5 -2
  14. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/parsing/common.py +12 -2
  15. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/parsing/trilogy.lark +1 -1
  16. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/LICENSE.md +0 -0
  17. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/pyproject.toml +0 -0
  18. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/pytrilogy.egg-info/SOURCES.txt +0 -0
  19. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/pytrilogy.egg-info/dependency_links.txt +0 -0
  20. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/pytrilogy.egg-info/entry_points.txt +0 -0
  21. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/pytrilogy.egg-info/requires.txt +0 -0
  22. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/pytrilogy.egg-info/top_level.txt +0 -0
  23. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/setup.cfg +0 -0
  24. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/setup.py +0 -0
  25. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_datatypes.py +0 -0
  26. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_declarations.py +0 -0
  27. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_derived_concepts.py +0 -0
  28. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_discovery_nodes.py +0 -0
  29. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_enums.py +0 -0
  30. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_environment.py +0 -0
  31. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_executor.py +0 -0
  32. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_imports.py +0 -0
  33. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_metadata.py +0 -0
  34. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_models.py +0 -0
  35. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_multi_join_assignments.py +0 -0
  36. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_parse_engine.py +0 -0
  37. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_parsing.py +0 -0
  38. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_partial_handling.py +0 -0
  39. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_query_processing.py +0 -0
  40. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_undefined_concept.py +0 -0
  41. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/tests/test_where_clause.py +0 -0
  42. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/compiler.py +0 -0
  43. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/constants.py +0 -0
  44. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/__init__.py +0 -0
  45. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/constants.py +0 -0
  46. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/enums.py +0 -0
  47. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/env_processor.py +0 -0
  48. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/environment_helpers.py +0 -0
  49. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/ergonomics.py +0 -0
  50. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/exceptions.py +0 -0
  51. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/functions.py +0 -0
  52. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/graph_models.py +0 -0
  53. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/internal.py +0 -0
  54. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/optimization.py +0 -0
  55. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/optimizations/__init__.py +0 -0
  56. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/optimizations/base_optimization.py +0 -0
  57. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/optimizations/inline_constant.py +0 -0
  58. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/optimizations/inline_datasource.py +0 -0
  59. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/__init__.py +0 -0
  60. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  61. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/graph_utils.py +0 -0
  62. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/__init__.py +0 -0
  63. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/common.py +0 -0
  64. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  65. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/group_node.py +0 -0
  66. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  67. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  68. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  69. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  70. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  71. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/select_node.py +0 -0
  72. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  73. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/nodes/__init__.py +0 -0
  74. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/nodes/base_node.py +0 -0
  75. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/nodes/filter_node.py +0 -0
  76. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/nodes/group_node.py +0 -0
  77. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/nodes/merge_node.py +0 -0
  78. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  79. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  80. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/nodes/window_node.py +0 -0
  81. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/processing/utility.py +0 -0
  82. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/core/query_processor.py +0 -0
  83. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/dialect/__init__.py +0 -0
  84. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/dialect/bigquery.py +0 -0
  85. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/dialect/common.py +0 -0
  86. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/dialect/config.py +0 -0
  87. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/dialect/duckdb.py +0 -0
  88. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/dialect/enums.py +0 -0
  89. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/dialect/postgres.py +0 -0
  90. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/dialect/presto.py +0 -0
  91. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/dialect/snowflake.py +0 -0
  92. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/dialect/sql_server.py +0 -0
  93. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/engine.py +0 -0
  94. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/executor.py +0 -0
  95. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/hooks/__init__.py +0 -0
  96. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/hooks/base_hook.py +0 -0
  97. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/hooks/graph_hook.py +0 -0
  98. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/hooks/query_debugger.py +0 -0
  99. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/metadata/__init__.py +0 -0
  100. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/parser.py +0 -0
  101. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/parsing/__init__.py +0 -0
  102. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/parsing/config.py +0 -0
  103. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/parsing/exceptions.py +0 -0
  104. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/parsing/helpers.py +0 -0
  105. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/parsing/parse_engine.py +0 -0
  106. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/parsing/render.py +0 -0
  107. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/py.typed +0 -0
  108. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/scripts/__init__.py +0 -0
  109. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/scripts/trilogy.py +0 -0
  110. {pytrilogy-0.0.2.40 → pytrilogy-0.0.2.42}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.40
3
+ Version: 0.0.2.42
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -186,7 +186,7 @@ datasource usa_names(
186
186
  gender:gender,
187
187
  state:state
188
188
  )
189
- address bigquery-public-data.usa_names.usa_1910_2013;
189
+ address `bigquery-public-data.usa_names.usa_1910_2013`;
190
190
 
191
191
  '''
192
192
  )
@@ -156,7 +156,7 @@ datasource usa_names(
156
156
  gender:gender,
157
157
  state:state
158
158
  )
159
- address bigquery-public-data.usa_names.usa_1910_2013;
159
+ address `bigquery-public-data.usa_names.usa_1910_2013`;
160
160
 
161
161
  '''
162
162
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.40
3
+ Version: 0.0.2.42
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -186,7 +186,7 @@ datasource usa_names(
186
186
  gender:gender,
187
187
  state:state
188
188
  )
189
- address bigquery-public-data.usa_names.usa_1910_2013;
189
+ address `bigquery-public-data.usa_names.usa_1910_2013`;
190
190
 
191
191
  '''
192
192
  )
@@ -89,6 +89,16 @@ def test_window_functions(test_environment):
89
89
  dialect.compile_statement(process_query(test_environment, select))
90
90
 
91
91
 
92
+ def test_window_datatype(test_environment):
93
+ declarations = """
94
+
95
+ auto category_rank <- rank category_name order by count(order_id) desc;
96
+
97
+ """
98
+ env, parsed = parse(declarations, environment=test_environment)
99
+ assert env.concepts["category_rank"].datatype == DataType.INTEGER
100
+
101
+
92
102
  def test_date_functions(test_environment):
93
103
  declarations = """
94
104
 
@@ -19,7 +19,7 @@ datasource posts (
19
19
  id: post_id
20
20
  )
21
21
  grain (post_id)
22
- address bigquery-public-data.stackoverflow.post_history
22
+ address `bigquery-public-data.stackoverflow.post_history`
23
23
  ;
24
24
 
25
25
 
@@ -29,7 +29,7 @@ datasource users (
29
29
  about_me: about_me,
30
30
  )
31
31
  grain (user_id)
32
- address bigquery-public-data.stackoverflow.users
32
+ address `bigquery-public-data.stackoverflow.users`
33
33
  ;
34
34
 
35
35
 
@@ -70,7 +70,7 @@ def test_double_aggregate():
70
70
  id: post_id
71
71
  )
72
72
  grain (post_id)
73
- address bigquery-public-data.stackoverflow.post_history
73
+ address `bigquery-public-data.stackoverflow.post_history`
74
74
  ;
75
75
 
76
76
 
@@ -80,7 +80,7 @@ def test_double_aggregate():
80
80
  about_me: about_me,
81
81
  )
82
82
  grain (user_id)
83
- address bigquery-public-data.stackoverflow.users
83
+ address `bigquery-public-data.stackoverflow.users`
84
84
  ;
85
85
 
86
86
 
@@ -16,7 +16,7 @@ def test_show_bigquery():
16
16
  id: post_id
17
17
  )
18
18
  grain (post_id)
19
- address bigquery-public-data.stackoverflow.post_history
19
+ address `bigquery-public-data.stackoverflow.post_history`
20
20
  ;
21
21
 
22
22
 
@@ -26,7 +26,7 @@ def test_show_bigquery():
26
26
  about_me: about_me,
27
27
  )
28
28
  grain (user_id)
29
- address bigquery-public-data.stackoverflow.users
29
+ address `bigquery-public-data.stackoverflow.users`
30
30
  ;
31
31
 
32
32
 
@@ -40,7 +40,7 @@ datasource posts (
40
40
  id: post_id
41
41
  )
42
42
  grain (post_id)
43
- address bigquery-public-data.stackoverflow.post_history
43
+ address `bigquery-public-data.stackoverflow.post_history`
44
44
  ;
45
45
  """
46
46
  parse(text)
@@ -76,7 +76,7 @@ datasource x_posts (
76
76
  id: post_id
77
77
  )
78
78
  grain (post_id)
79
- address bigquery-public-data.stackoverflow.post_history
79
+ address `bigquery-public-data.stackoverflow.post_history`
80
80
  where post_id = 2
81
81
  ;
82
82
  """
@@ -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.40"
7
+ __version__ = "0.0.2.42"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -1375,6 +1375,9 @@ class WindowItem(Mergeable, Namespaced, SelectContext, BaseModel):
1375
1375
  over: List["Concept"] = Field(default_factory=list)
1376
1376
  index: Optional[int] = None
1377
1377
 
1378
+ def __repr__(self) -> str:
1379
+ return f"{self.type}({self.content} {self.index}, {self.over}, {self.order_by})"
1380
+
1378
1381
  def with_merge(
1379
1382
  self, source: Concept, target: Concept, modifiers: List[Modifier]
1380
1383
  ) -> "WindowItem":
@@ -1383,6 +1386,7 @@ class WindowItem(Mergeable, Namespaced, SelectContext, BaseModel):
1383
1386
  content=self.content.with_merge(source, target, modifiers),
1384
1387
  over=[x.with_merge(source, target, modifiers) for x in self.over],
1385
1388
  order_by=[x.with_merge(source, target, modifiers) for x in self.order_by],
1389
+ index=self.index,
1386
1390
  )
1387
1391
 
1388
1392
  def with_namespace(self, namespace: str) -> "WindowItem":
@@ -1391,6 +1395,7 @@ class WindowItem(Mergeable, Namespaced, SelectContext, BaseModel):
1391
1395
  content=self.content.with_namespace(namespace),
1392
1396
  over=[x.with_namespace(namespace) for x in self.over],
1393
1397
  order_by=[x.with_namespace(namespace) for x in self.order_by],
1398
+ index=self.index,
1394
1399
  )
1395
1400
 
1396
1401
  def with_select_context(
@@ -1410,6 +1415,7 @@ class WindowItem(Mergeable, Namespaced, SelectContext, BaseModel):
1410
1415
  x.with_select_context(grain, conditional, environment)
1411
1416
  for x in self.order_by
1412
1417
  ],
1418
+ index=self.index,
1413
1419
  )
1414
1420
 
1415
1421
  @property
@@ -6,6 +6,7 @@ from trilogy.core.models import (
6
6
  ConceptArgs,
7
7
  Comparison,
8
8
  Parenthetical,
9
+ WindowItem,
9
10
  )
10
11
  from trilogy.core.optimizations.base_optimization import OptimizationRule
11
12
  from trilogy.core.processing.utility import is_scalar_condition
@@ -45,7 +46,15 @@ class PredicatePushdown(OptimizationRule):
45
46
  all_inputs = {x.address for x in candidate.concept_arguments}
46
47
  if is_child_of(candidate, parent_cte.condition):
47
48
  return False
48
-
49
+ non_materialized = [k for k, v in parent_cte.source_map.items() if v == []]
50
+ concrete = [
51
+ x for x in parent_cte.output_columns if x.address in non_materialized
52
+ ]
53
+ if any(isinstance(x.lineage, WindowItem) for x in concrete):
54
+ self.debug(
55
+ f"CTE {parent_cte.name} has window clause calculation, cannot push up to this without changing results"
56
+ )
57
+ return False
49
58
  materialized = {k for k, v in parent_cte.source_map.items() if v != []}
50
59
  if not row_conditions or not materialized:
51
60
  return False
@@ -108,12 +117,12 @@ class PredicatePushdown(OptimizationRule):
108
117
 
109
118
  if not cte.parent_ctes:
110
119
  self.debug(f"No parent CTEs for {cte.name}")
111
-
112
120
  return False
113
121
 
114
122
  if not cte.condition:
115
123
  self.debug(f"No CTE condition for {cte.name}")
116
124
  return False
125
+
117
126
  if self.complete.get(cte.name):
118
127
  self.debug("Have done this CTE before")
119
128
  return False
@@ -1,7 +1,12 @@
1
1
  # directly select out a basic derivation
2
2
  from typing import List
3
3
 
4
- from trilogy.core.models import Concept, WhereClause
4
+ from trilogy.core.models import (
5
+ Concept,
6
+ WhereClause,
7
+ Function,
8
+ FunctionClass,
9
+ )
5
10
  from trilogy.core.processing.nodes import StrategyNode, History
6
11
  from trilogy.core.processing.node_generators.common import (
7
12
  resolve_function_parent_concepts,
@@ -12,6 +17,22 @@ from trilogy.core.enums import SourceType
12
17
  LOGGER_PREFIX = "[GEN_BASIC_NODE]"
13
18
 
14
19
 
20
+ def is_equivalent_basic_function_lineage(
21
+ x: Concept,
22
+ y: Concept,
23
+ ):
24
+ if not isinstance(x.lineage, Function) or not isinstance(y.lineage, Function):
25
+ return False
26
+ if x.lineage.operator == y.lineage.operator:
27
+ return True
28
+ if (
29
+ y.lineage.operator in FunctionClass.AGGREGATE_FUNCTIONS.value
30
+ or y.lineage.operator in FunctionClass.ONE_TO_MANY.value
31
+ ):
32
+ return False
33
+ return True
34
+
35
+
15
36
  def gen_basic_node(
16
37
  concept: Concept,
17
38
  local_optional: List[Concept],
@@ -32,8 +53,15 @@ def gen_basic_node(
32
53
  equivalent_optional = [
33
54
  x
34
55
  for x in local_optional
35
- if x.lineage == concept.lineage and x.address != concept.address
56
+ if is_equivalent_basic_function_lineage(concept, x)
57
+ and x.address != concept.address
36
58
  ]
59
+ if equivalent_optional:
60
+ logger.info(
61
+ f"{depth_prefix}{LOGGER_PREFIX} basic node for {concept} has equivalent optional {[x.address for x in equivalent_optional]}"
62
+ )
63
+ for eo in equivalent_optional:
64
+ parent_concepts += resolve_function_parent_concepts(eo)
37
65
  non_equivalent_optional = [
38
66
  x for x in local_optional if x not in equivalent_optional
39
67
  ]
@@ -10,16 +10,23 @@ from trilogy.core.processing.utility import padding
10
10
  LOGGER_PREFIX = "[GEN_WINDOW_NODE]"
11
11
 
12
12
 
13
- def resolve_window_parent_concepts(concept: Concept) -> List[Concept]:
13
+ def resolve_window_parent_concepts(
14
+ concept: Concept, environment: Environment
15
+ ) -> tuple[Concept, List[Concept]]:
14
16
  if not isinstance(concept.lineage, WindowItem):
15
17
  raise ValueError
16
- base = [concept.lineage.content]
18
+ base = []
17
19
  if concept.lineage.over:
18
20
  base += concept.lineage.over
19
21
  if concept.lineage.order_by:
20
22
  for item in concept.lineage.order_by:
23
+ # TODO: we do want to use the rehydrated value, but
24
+ # that introduces a circular dependency on an aggregate
25
+ # that is grouped by a window
26
+ # need to figure out how to resolve this
27
+ # base += [environment.concepts[item.expr.output.address]]
21
28
  base += [item.expr.output]
22
- return unique(base, "address")
29
+ return concept.lineage.content, unique(base, "address")
23
30
 
24
31
 
25
32
  def gen_window_node(
@@ -32,9 +39,25 @@ def gen_window_node(
32
39
  history: History | None = None,
33
40
  conditions: WhereClause | None = None,
34
41
  ) -> StrategyNode | None:
35
- parent_concepts = resolve_window_parent_concepts(concept)
36
- parent_node = source_concepts(
37
- mandatory_list=parent_concepts + local_optional,
42
+ base, parent_concepts = resolve_window_parent_concepts(concept, environment)
43
+ equivalent_optional = [
44
+ x
45
+ for x in local_optional
46
+ if isinstance(x.lineage, WindowItem)
47
+ and resolve_window_parent_concepts(x, environment)[1] == parent_concepts
48
+ ]
49
+
50
+ non_equivalent_optional = [
51
+ x for x in local_optional if x.address not in equivalent_optional
52
+ ]
53
+ targets = [base]
54
+ if equivalent_optional:
55
+ for x in equivalent_optional:
56
+ assert isinstance(x.lineage, WindowItem)
57
+ targets.append(x.lineage.content)
58
+
59
+ parent_node: StrategyNode = source_concepts(
60
+ mandatory_list=parent_concepts + targets + non_equivalent_optional,
38
61
  environment=environment,
39
62
  g=g,
40
63
  depth=depth + 1,
@@ -61,7 +84,7 @@ def gen_window_node(
61
84
  )
62
85
  raise SyntaxError
63
86
  _window_node = WindowNode(
64
- input_concepts=parent_concepts + local_optional,
87
+ input_concepts=parent_concepts + targets + non_equivalent_optional,
65
88
  output_concepts=[concept] + parent_concepts + local_optional,
66
89
  environment=environment,
67
90
  g=g,
@@ -75,7 +75,7 @@ def window_factory(string: str, include_concept: bool = False) -> Callable:
75
75
  ) -> str:
76
76
  if not include_concept:
77
77
  concept = ""
78
- if offset:
78
+ if offset is not None:
79
79
  base = f"{string}({concept}, {offset})"
80
80
  else:
81
81
  base = f"{string}({concept})"
@@ -313,7 +313,10 @@ class BaseDialect:
313
313
  )
314
314
  for x in c.lineage.over
315
315
  ]
316
- rval = f"{self.WINDOW_FUNCTION_MAP[c.lineage.type](concept = self.render_concept_sql(c.lineage.content, cte=cte, alias=False, raise_invalid=raise_invalid), window=','.join(rendered_over_components), sort=','.join(rendered_order_components))}" # noqa: E501
316
+ rval = f"{self.WINDOW_FUNCTION_MAP[c.lineage.type](concept = self.render_concept_sql(c.lineage.content,
317
+ cte=cte, alias=False, raise_invalid=raise_invalid),
318
+ window=','.join(rendered_over_components), sort=','.join(rendered_order_components),
319
+ offset=c.lineage.index)}" # noqa: E501
317
320
  elif isinstance(c.lineage, FilterItem):
318
321
  # for cases when we've optimized this
319
322
  if cte.condition == c.lineage.where.conditional:
@@ -13,6 +13,7 @@ from trilogy.core.models import (
13
13
  Parenthetical,
14
14
  FunctionClass,
15
15
  Environment,
16
+ DataType,
16
17
  )
17
18
  from typing import List, Tuple
18
19
  from trilogy.core.functions import (
@@ -25,7 +26,7 @@ from trilogy.core.enums import PurposeLineage
25
26
  from trilogy.constants import (
26
27
  VIRTUAL_CONCEPT_PREFIX,
27
28
  )
28
- from trilogy.core.enums import Modifier
29
+ from trilogy.core.enums import Modifier, WindowType
29
30
 
30
31
 
31
32
  def get_upstream_modifiers(keys: List[Concept]) -> list[Modifier]:
@@ -234,9 +235,18 @@ def window_item_to_concept(
234
235
  else:
235
236
  grain = parent.over + [parent.content.output]
236
237
  modifiers = get_upstream_modifiers(parent.content.concept_arguments)
238
+ datatype = parent.content.datatype
239
+ if parent.type in (
240
+ WindowType.RANK,
241
+ WindowType.ROW_NUMBER,
242
+ WindowType.COUNT,
243
+ WindowType.COUNT_DISTINCT,
244
+ ):
245
+ datatype = DataType.INTEGER
246
+
237
247
  return Concept(
238
248
  name=name,
239
- datatype=parent.content.datatype,
249
+ datatype=datatype,
240
250
  purpose=local_purpose,
241
251
  lineage=parent,
242
252
  metadata=fmetadata,
@@ -290,7 +290,7 @@
290
290
 
291
291
  // base language constructs
292
292
  concept_lit: IDENTIFIER
293
- IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_\-\.]*/
293
+ IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_\.]*/
294
294
  WILDCARD_IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_\-\.\*]*/
295
295
  QUOTED_IDENTIFIER: /`[a-zA-Z\_][a-zA-Z0-9\_\.\-\*\:\s]*`/
296
296
  QUOTED_ADDRESS: /`[a-zA-Z\_][a-zA-Z0-9\_\.\-\*\:]*`/
File without changes
File without changes
File without changes