pytrilogy 0.0.2.28__tar.gz → 0.0.2.29__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.
- {pytrilogy-0.0.2.28/pytrilogy.egg-info → pytrilogy-0.0.2.29}/PKG-INFO +1 -1
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_parsing.py +30 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/models.py +0 -1
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/optimization.py +1 -15
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/concept_strategies_v3.py +2 -3
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/select_merge_node.py +16 -2
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/nodes/base_node.py +0 -3
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/nodes/select_node_v2.py +1 -4
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/utility.py +38 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/dialect/base.py +13 -9
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/LICENSE.md +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/README.md +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/pyproject.toml +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/pytrilogy.egg-info/SOURCES.txt +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/setup.cfg +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/setup.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_models.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_select.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_show.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/enums.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/functions.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/optimizations/inline_constant.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/parsing/common.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/parsing/parse_engine.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/parsing/trilogy.lark +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/utility.py +0 -0
|
@@ -7,6 +7,7 @@ from trilogy.core.models import (
|
|
|
7
7
|
Environment,
|
|
8
8
|
Comparison,
|
|
9
9
|
TupleWrapper,
|
|
10
|
+
Datasource,
|
|
10
11
|
)
|
|
11
12
|
from trilogy.core.functions import argument_to_purpose, function_args_to_output_purpose
|
|
12
13
|
from trilogy.parsing.parse_engine import (
|
|
@@ -511,6 +512,35 @@ address `abc:def`
|
|
|
511
512
|
assert ds.non_partial_for.conditional.right == 10
|
|
512
513
|
|
|
513
514
|
|
|
515
|
+
def test_datasource_from_persist():
|
|
516
|
+
|
|
517
|
+
text = """
|
|
518
|
+
key x int;
|
|
519
|
+
key y int;
|
|
520
|
+
|
|
521
|
+
datasource test (
|
|
522
|
+
x:x,
|
|
523
|
+
y:y)
|
|
524
|
+
grain(x)
|
|
525
|
+
address `abc:def`
|
|
526
|
+
;
|
|
527
|
+
|
|
528
|
+
persist alias into tbl_alias from
|
|
529
|
+
select
|
|
530
|
+
x,
|
|
531
|
+
y
|
|
532
|
+
where y>10;
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
"""
|
|
537
|
+
env, parsed = parse_text(text)
|
|
538
|
+
|
|
539
|
+
ds: Datasource = parsed[-1].datasource
|
|
540
|
+
assert ds.non_partial_for.conditional.right == 10
|
|
541
|
+
assert not ds.where
|
|
542
|
+
|
|
543
|
+
|
|
514
544
|
def test_filter_concise():
|
|
515
545
|
|
|
516
546
|
text = """
|
|
@@ -1760,7 +1760,6 @@ class SelectStatement(HasUUID, Mergeable, Namespaced, SelectTypeMixin, BaseModel
|
|
|
1760
1760
|
grain=grain or self.grain,
|
|
1761
1761
|
columns=columns,
|
|
1762
1762
|
namespace=namespace,
|
|
1763
|
-
where=WhereClause(conditional=condition) if condition else None,
|
|
1764
1763
|
non_partial_for=WhereClause(conditional=condition) if condition else None,
|
|
1765
1764
|
)
|
|
1766
1765
|
for column in columns:
|
|
@@ -13,7 +13,7 @@ from trilogy.core.optimizations import (
|
|
|
13
13
|
PredicatePushdownRemove,
|
|
14
14
|
InlineDatasource,
|
|
15
15
|
)
|
|
16
|
-
|
|
16
|
+
from trilogy.core.processing.utility import sort_select_output
|
|
17
17
|
|
|
18
18
|
MAX_OPTIMIZATION_LOOPS = 100
|
|
19
19
|
|
|
@@ -154,20 +154,6 @@ def is_direct_return_eligible(cte: CTE) -> CTE | None:
|
|
|
154
154
|
return direct_parent
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
def sort_select_output(cte: CTE, query: SelectStatement | MultiSelectStatement):
|
|
158
|
-
hidden_addresses = [c.address for c in query.hidden_components]
|
|
159
|
-
output_addresses = [
|
|
160
|
-
c.address for c in query.output_components if c.address not in hidden_addresses
|
|
161
|
-
]
|
|
162
|
-
|
|
163
|
-
mapping = {x.address: x for x in cte.output_columns}
|
|
164
|
-
|
|
165
|
-
new_output = []
|
|
166
|
-
for x in output_addresses:
|
|
167
|
-
new_output.append(mapping[x])
|
|
168
|
-
cte.output_columns = new_output
|
|
169
|
-
|
|
170
|
-
|
|
171
157
|
def optimize_ctes(
|
|
172
158
|
input: list[CTE], root_cte: CTE, select: SelectStatement | MultiSelectStatement
|
|
173
159
|
) -> list[CTE]:
|
|
@@ -838,9 +838,8 @@ def _search_concepts(
|
|
|
838
838
|
f" {accept_partial} (complete: {complete}), have {found} from {[n for n in stack]} (missing {missing} partial {partial} virtual {virtual}), attempted {attempted}, mandatory w/ filter {mandatory_completion}"
|
|
839
839
|
)
|
|
840
840
|
if complete == ValidationResult.INCOMPLETE_CONDITION:
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
)
|
|
841
|
+
cond_dict = {str(node): node.preexisting_conditions for node in stack}
|
|
842
|
+
raise SyntaxError(f"Have {cond_dict} and need {str(conditions)}")
|
|
844
843
|
# early exit if we have a complete stack with one node
|
|
845
844
|
# we can only early exit if we have a complete stack
|
|
846
845
|
# and we are not looking for more non-partial sources
|
|
@@ -233,7 +233,6 @@ def create_select_node(
|
|
|
233
233
|
]
|
|
234
234
|
nullable_lcl = LooseConceptList(concepts=nullable_concepts)
|
|
235
235
|
partial_is_full = conditions and (conditions == datasource.non_partial_for)
|
|
236
|
-
|
|
237
236
|
bcandidate: StrategyNode = SelectNode(
|
|
238
237
|
input_concepts=[c.concept for c in datasource.columns],
|
|
239
238
|
output_concepts=all_concepts,
|
|
@@ -249,7 +248,9 @@ def create_select_node(
|
|
|
249
248
|
datasource=datasource,
|
|
250
249
|
grain=Grain(components=all_concepts),
|
|
251
250
|
conditions=datasource.where.conditional if datasource.where else None,
|
|
252
|
-
|
|
251
|
+
preexisting_conditions=(
|
|
252
|
+
conditions.conditional if partial_is_full and conditions else None
|
|
253
|
+
),
|
|
253
254
|
)
|
|
254
255
|
|
|
255
256
|
# we need to nest the group node one further
|
|
@@ -263,6 +264,9 @@ def create_select_node(
|
|
|
263
264
|
depth=depth,
|
|
264
265
|
partial_concepts=bcandidate.partial_concepts,
|
|
265
266
|
nullable_concepts=bcandidate.nullable_concepts,
|
|
267
|
+
preexisting_conditions=(
|
|
268
|
+
conditions.conditional if partial_is_full and conditions else None
|
|
269
|
+
),
|
|
266
270
|
)
|
|
267
271
|
else:
|
|
268
272
|
candidate = bcandidate
|
|
@@ -340,6 +344,15 @@ def gen_select_merge_node(
|
|
|
340
344
|
|
|
341
345
|
if len(parents) == 1:
|
|
342
346
|
return parents[0]
|
|
347
|
+
preexisting_conditions = None
|
|
348
|
+
if conditions and all(
|
|
349
|
+
[
|
|
350
|
+
x.preexisting_conditions
|
|
351
|
+
and x.preexisting_conditions == conditions.conditional
|
|
352
|
+
for x in parents
|
|
353
|
+
]
|
|
354
|
+
):
|
|
355
|
+
preexisting_conditions = conditions.conditional
|
|
343
356
|
return MergeNode(
|
|
344
357
|
output_concepts=all_concepts,
|
|
345
358
|
input_concepts=non_constant,
|
|
@@ -347,4 +360,5 @@ def gen_select_merge_node(
|
|
|
347
360
|
g=g,
|
|
348
361
|
depth=depth,
|
|
349
362
|
parents=parents,
|
|
363
|
+
preexisting_conditions=preexisting_conditions,
|
|
350
364
|
)
|
|
@@ -165,7 +165,6 @@ class StrategyNode:
|
|
|
165
165
|
hidden_concepts: List[Concept] | None = None,
|
|
166
166
|
existence_concepts: List[Concept] | None = None,
|
|
167
167
|
virtual_output_concepts: List[Concept] | None = None,
|
|
168
|
-
render_condition: bool = True,
|
|
169
168
|
):
|
|
170
169
|
self.input_concepts: List[Concept] = (
|
|
171
170
|
unique(input_concepts, "address") if input_concepts else []
|
|
@@ -209,7 +208,6 @@ class StrategyNode:
|
|
|
209
208
|
)
|
|
210
209
|
self.validate_parents()
|
|
211
210
|
self.log = True
|
|
212
|
-
self.render_condition = render_condition
|
|
213
211
|
|
|
214
212
|
def add_parents(self, parents: list["StrategyNode"]):
|
|
215
213
|
self.parents += parents
|
|
@@ -382,7 +380,6 @@ class StrategyNode:
|
|
|
382
380
|
hidden_concepts=list(self.hidden_concepts),
|
|
383
381
|
existence_concepts=list(self.existence_concepts),
|
|
384
382
|
virtual_output_concepts=list(self.virtual_output_concepts),
|
|
385
|
-
render_condition=self.render_condition,
|
|
386
383
|
)
|
|
387
384
|
|
|
388
385
|
|
|
@@ -49,7 +49,6 @@ class SelectNode(StrategyNode):
|
|
|
49
49
|
conditions: Conditional | Comparison | Parenthetical | None = None,
|
|
50
50
|
preexisting_conditions: Conditional | Comparison | Parenthetical | None = None,
|
|
51
51
|
hidden_concepts: List[Concept] | None = None,
|
|
52
|
-
render_condition: bool = True,
|
|
53
52
|
):
|
|
54
53
|
super().__init__(
|
|
55
54
|
input_concepts=input_concepts,
|
|
@@ -66,7 +65,6 @@ class SelectNode(StrategyNode):
|
|
|
66
65
|
conditions=conditions,
|
|
67
66
|
preexisting_conditions=preexisting_conditions,
|
|
68
67
|
hidden_concepts=hidden_concepts,
|
|
69
|
-
render_condition=render_condition,
|
|
70
68
|
)
|
|
71
69
|
self.accept_partial = accept_partial
|
|
72
70
|
self.datasource = datasource
|
|
@@ -123,7 +121,7 @@ class SelectNode(StrategyNode):
|
|
|
123
121
|
nullable_concepts=[c.concept for c in datasource.columns if c.is_nullable],
|
|
124
122
|
source_type=SourceType.DIRECT_SELECT,
|
|
125
123
|
# we can skip rendering conditions
|
|
126
|
-
condition=self.conditions
|
|
124
|
+
condition=self.conditions,
|
|
127
125
|
# select nodes should never group
|
|
128
126
|
force_group=self.force_group,
|
|
129
127
|
hidden_concepts=self.hidden_concepts,
|
|
@@ -208,7 +206,6 @@ class SelectNode(StrategyNode):
|
|
|
208
206
|
conditions=self.conditions,
|
|
209
207
|
preexisting_conditions=self.preexisting_conditions,
|
|
210
208
|
hidden_concepts=self.hidden_concepts,
|
|
211
|
-
render_condition=self.render_condition,
|
|
212
209
|
)
|
|
213
210
|
|
|
214
211
|
|
|
@@ -29,6 +29,10 @@ from trilogy.core.models import (
|
|
|
29
29
|
NumericType,
|
|
30
30
|
ListType,
|
|
31
31
|
TupleWrapper,
|
|
32
|
+
CTE,
|
|
33
|
+
MultiSelectStatement,
|
|
34
|
+
SelectStatement,
|
|
35
|
+
ProcessedQuery,
|
|
32
36
|
)
|
|
33
37
|
|
|
34
38
|
from trilogy.core.enums import Purpose, Granularity, BooleanOperator
|
|
@@ -528,3 +532,37 @@ def find_nullable_concepts(
|
|
|
528
532
|
if set(v).issubset(all_ds):
|
|
529
533
|
final_nullable.add(k)
|
|
530
534
|
return list(sorted(final_nullable))
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def sort_select_output_processed(cte: CTE, query: ProcessedQuery) -> CTE:
|
|
538
|
+
hidden_addresses = [c.address for c in query.hidden_columns]
|
|
539
|
+
output_addresses = [
|
|
540
|
+
c.address for c in query.output_columns if c.address not in hidden_addresses
|
|
541
|
+
]
|
|
542
|
+
|
|
543
|
+
mapping = {x.address: x for x in cte.output_columns}
|
|
544
|
+
|
|
545
|
+
new_output = []
|
|
546
|
+
for x in output_addresses:
|
|
547
|
+
new_output.append(mapping[x])
|
|
548
|
+
cte.output_columns = new_output
|
|
549
|
+
return cte
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def sort_select_output(
|
|
553
|
+
cte: CTE, query: SelectStatement | MultiSelectStatement | ProcessedQuery
|
|
554
|
+
) -> CTE:
|
|
555
|
+
if isinstance(query, ProcessedQuery):
|
|
556
|
+
return sort_select_output_processed(cte, query)
|
|
557
|
+
hidden_addresses = [c.address for c in query.hidden_components]
|
|
558
|
+
output_addresses = [
|
|
559
|
+
c.address for c in query.output_components if c.address not in hidden_addresses
|
|
560
|
+
]
|
|
561
|
+
|
|
562
|
+
mapping = {x.address: x for x in cte.output_columns}
|
|
563
|
+
|
|
564
|
+
new_output = []
|
|
565
|
+
for x in output_addresses:
|
|
566
|
+
new_output.append(mapping[x])
|
|
567
|
+
cte.output_columns = new_output
|
|
568
|
+
return cte
|
|
@@ -2,7 +2,11 @@ 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
|
|
5
|
+
from trilogy.core.processing.utility import (
|
|
6
|
+
is_scalar_condition,
|
|
7
|
+
decompose_condition,
|
|
8
|
+
sort_select_output,
|
|
9
|
+
)
|
|
6
10
|
from trilogy.constants import CONFIG, logger, MagicConstants
|
|
7
11
|
from trilogy.core.internal import DEFAULT_CONCEPTS
|
|
8
12
|
from trilogy.core.enums import (
|
|
@@ -537,7 +541,7 @@ class BaseDialect:
|
|
|
537
541
|
else:
|
|
538
542
|
raise ValueError(f"Unable to render type {type(e)} {e}")
|
|
539
543
|
|
|
540
|
-
def render_cte(self, cte: CTE):
|
|
544
|
+
def render_cte(self, cte: CTE, auto_sort: bool = True):
|
|
541
545
|
if self.UNNEST_MODE in (
|
|
542
546
|
UnnestMode.CROSS_APPLY,
|
|
543
547
|
UnnestMode.CROSS_JOIN,
|
|
@@ -561,6 +565,8 @@ class BaseDialect:
|
|
|
561
565
|
for c in cte.output_columns
|
|
562
566
|
if c.address not in [y.address for y in cte.hidden_concepts]
|
|
563
567
|
]
|
|
568
|
+
if auto_sort:
|
|
569
|
+
select_columns = sorted(select_columns, key=lambda x: x)
|
|
564
570
|
source: str | None = cte.base_name
|
|
565
571
|
if not cte.render_from_clause:
|
|
566
572
|
if len(cte.joins) > 0:
|
|
@@ -657,8 +663,11 @@ class BaseDialect:
|
|
|
657
663
|
def generate_ctes(
|
|
658
664
|
self,
|
|
659
665
|
query: ProcessedQuery,
|
|
660
|
-
):
|
|
661
|
-
return [self.render_cte(cte) for cte in query.ctes]
|
|
666
|
+
) -> List[CompiledCTE]:
|
|
667
|
+
return [self.render_cte(cte) for cte in query.ctes[:-1]] + [
|
|
668
|
+
# last CTE needs to respect the user output order
|
|
669
|
+
self.render_cte(sort_select_output(query.ctes[-1], query), auto_sort=False)
|
|
670
|
+
]
|
|
662
671
|
|
|
663
672
|
def generate_queries(
|
|
664
673
|
self,
|
|
@@ -790,11 +799,6 @@ class BaseDialect:
|
|
|
790
799
|
|
|
791
800
|
compiled_ctes = self.generate_ctes(query)
|
|
792
801
|
|
|
793
|
-
# restort selections by the order they were written in
|
|
794
|
-
sorted_select: List[str] = []
|
|
795
|
-
for output_c in output_addresses:
|
|
796
|
-
sorted_select.append(select_columns[output_c])
|
|
797
|
-
|
|
798
802
|
final = self.SQL_TEMPLATE.render(
|
|
799
803
|
output=(
|
|
800
804
|
query.output_to if isinstance(query, ProcessedQueryPersist) else None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/__init__.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/basic_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/filter_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/group_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/group_to_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/node_merge_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/rowset_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/select_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/unnest_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.2.28 → pytrilogy-0.0.2.29}/trilogy/core/processing/node_generators/window_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|