pytrilogy 0.0.1.107__tar.gz → 0.0.1.109__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.1.107/pytrilogy.egg-info → pytrilogy-0.0.1.109}/PKG-INFO +1 -1
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/pytrilogy.egg-info/SOURCES.txt +2 -3
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/env_processor.py +2 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/models.py +40 -13
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/optimization.py +1 -1
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/concept_strategies_v3.py +11 -4
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/__init__.py +2 -2
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/common.py +1 -0
- pytrilogy-0.0.1.107/trilogy/core/processing/node_generators/concept_merge.py → pytrilogy-0.0.1.109/trilogy/core/processing/node_generators/concept_merge_node.py +57 -11
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/rowset_node.py +9 -3
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/nodes/__init__.py +10 -4
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/nodes/base_node.py +3 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/nodes/merge_node.py +4 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/query_processor.py +1 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/dialect/base.py +4 -1
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/dialect/config.py +1 -16
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/dialect/presto.py +5 -3
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/parsing/parse_engine.py +4 -4
- pytrilogy-0.0.1.107/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/LICENSE.md +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/README.md +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/pyproject.toml +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/setup.cfg +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/setup.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_models.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_select.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/enums.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/functions.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- /pytrilogy-0.0.1.107/trilogy/core/processing/node_generators/merge_node.py → /pytrilogy-0.0.1.109/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.1.107/trilogy/docs → pytrilogy-0.0.1.109/trilogy/hooks}/__init__.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.1.107/trilogy/hooks → pytrilogy-0.0.1.109/trilogy/metadata}/__init__.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.1.107/trilogy/metadata → pytrilogy-0.0.1.109/trilogy/parsing}/__init__.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/parsing/common.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.1.107/trilogy/parsing → pytrilogy-0.0.1.109/trilogy/scripts}/__init__.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/utility.py +0 -0
|
@@ -52,12 +52,12 @@ trilogy/core/processing/utility.py
|
|
|
52
52
|
trilogy/core/processing/node_generators/__init__.py
|
|
53
53
|
trilogy/core/processing/node_generators/basic_node.py
|
|
54
54
|
trilogy/core/processing/node_generators/common.py
|
|
55
|
-
trilogy/core/processing/node_generators/
|
|
55
|
+
trilogy/core/processing/node_generators/concept_merge_node.py
|
|
56
56
|
trilogy/core/processing/node_generators/filter_node.py
|
|
57
57
|
trilogy/core/processing/node_generators/group_node.py
|
|
58
58
|
trilogy/core/processing/node_generators/group_to_node.py
|
|
59
|
-
trilogy/core/processing/node_generators/merge_node.py
|
|
60
59
|
trilogy/core/processing/node_generators/multiselect_node.py
|
|
60
|
+
trilogy/core/processing/node_generators/node_merge_node.py
|
|
61
61
|
trilogy/core/processing/node_generators/rowset_node.py
|
|
62
62
|
trilogy/core/processing/node_generators/select_node.py
|
|
63
63
|
trilogy/core/processing/node_generators/unnest_node.py
|
|
@@ -81,7 +81,6 @@ trilogy/dialect/postgres.py
|
|
|
81
81
|
trilogy/dialect/presto.py
|
|
82
82
|
trilogy/dialect/snowflake.py
|
|
83
83
|
trilogy/dialect/sql_server.py
|
|
84
|
-
trilogy/docs/__init__.py
|
|
85
84
|
trilogy/hooks/__init__.py
|
|
86
85
|
trilogy/hooks/base_hook.py
|
|
87
86
|
trilogy/hooks/graph_hook.py
|
|
@@ -21,6 +21,8 @@ def generate_graph(
|
|
|
21
21
|
for source in concept.sources:
|
|
22
22
|
generic = source.with_default_grain()
|
|
23
23
|
g.add_edge(generic, node_name)
|
|
24
|
+
|
|
25
|
+
# link the concept to the generic source
|
|
24
26
|
if concept.derivation == PurposeLineage.MERGE:
|
|
25
27
|
g.add_edge(node_name, generic)
|
|
26
28
|
for _, dataset in environment.datasources.items():
|
|
@@ -496,13 +496,31 @@ class Concept(Namespaced, SelectGrain, BaseModel):
|
|
|
496
496
|
@property
|
|
497
497
|
def sources(self) -> List["Concept"]:
|
|
498
498
|
if self.lineage:
|
|
499
|
-
output = []
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
499
|
+
output: List[Concept] = []
|
|
500
|
+
|
|
501
|
+
def get_sources(
|
|
502
|
+
expr: Union[
|
|
503
|
+
Function,
|
|
504
|
+
WindowItem,
|
|
505
|
+
FilterItem,
|
|
506
|
+
AggregateWrapper,
|
|
507
|
+
RowsetItem,
|
|
508
|
+
MultiSelectStatement | MergeStatement,
|
|
509
|
+
],
|
|
510
|
+
output: List[Concept],
|
|
511
|
+
):
|
|
512
|
+
for item in expr.arguments:
|
|
513
|
+
if isinstance(item, Concept):
|
|
514
|
+
if item.address == self.address:
|
|
515
|
+
raise SyntaxError(
|
|
516
|
+
f"Concept {self.address} references itself"
|
|
517
|
+
)
|
|
518
|
+
output.append(item)
|
|
519
|
+
output += item.sources
|
|
520
|
+
elif isinstance(item, Function):
|
|
521
|
+
get_sources(item, output)
|
|
522
|
+
|
|
523
|
+
get_sources(self.lineage, output)
|
|
506
524
|
return output
|
|
507
525
|
return []
|
|
508
526
|
|
|
@@ -1512,7 +1530,7 @@ class MergeStatement(Namespaced, BaseModel):
|
|
|
1512
1530
|
if z.address == x.address:
|
|
1513
1531
|
return z
|
|
1514
1532
|
raise SyntaxError(
|
|
1515
|
-
f"Could not find upstream map for multiselect {str(concept)} on cte ({cte})"
|
|
1533
|
+
f"Could not find upstream map for multiselect {str(concept)} on cte ({cte.name})"
|
|
1516
1534
|
)
|
|
1517
1535
|
|
|
1518
1536
|
def with_namespace(self, namespace: str) -> "MergeStatement":
|
|
@@ -1779,6 +1797,7 @@ class QueryDatasource(BaseModel):
|
|
|
1779
1797
|
source_type: SourceType = SourceType.SELECT
|
|
1780
1798
|
partial_concepts: List[Concept] = Field(default_factory=list)
|
|
1781
1799
|
join_derived_concepts: List[Concept] = Field(default_factory=list)
|
|
1800
|
+
hidden_concepts: List[Concept] = Field(default_factory=list)
|
|
1782
1801
|
force_group: bool | None = None
|
|
1783
1802
|
|
|
1784
1803
|
@property
|
|
@@ -1997,6 +2016,7 @@ class CTE(BaseModel):
|
|
|
1997
2016
|
condition: Optional[Union["Conditional", "Comparison", "Parenthetical"]] = None
|
|
1998
2017
|
partial_concepts: List[Concept] = Field(default_factory=list)
|
|
1999
2018
|
join_derived_concepts: List[Concept] = Field(default_factory=list)
|
|
2019
|
+
hidden_concepts: List[Concept] = Field(default_factory=list)
|
|
2000
2020
|
order_by: Optional[OrderBy] = None
|
|
2001
2021
|
limit: Optional[int] = None
|
|
2002
2022
|
requires_nesting: bool = True
|
|
@@ -2433,6 +2453,7 @@ class Environment(BaseModel):
|
|
|
2433
2453
|
cte_name_map: Dict[str, str] = Field(default_factory=dict)
|
|
2434
2454
|
|
|
2435
2455
|
materialized_concepts: List[Concept] = Field(default_factory=list)
|
|
2456
|
+
merged_concepts: Dict[str, Concept] = Field(default_factory=dict)
|
|
2436
2457
|
_parse_count: int = 0
|
|
2437
2458
|
|
|
2438
2459
|
@classmethod
|
|
@@ -2460,7 +2481,7 @@ class Environment(BaseModel):
|
|
|
2460
2481
|
f.write(self.model_dump_json())
|
|
2461
2482
|
return ppath
|
|
2462
2483
|
|
|
2463
|
-
def
|
|
2484
|
+
def gen_concept_list_caches(self) -> None:
|
|
2464
2485
|
concrete_addresses = set()
|
|
2465
2486
|
for datasource in self.datasources.values():
|
|
2466
2487
|
for concept in datasource.output_concepts:
|
|
@@ -2468,6 +2489,12 @@ class Environment(BaseModel):
|
|
|
2468
2489
|
self.materialized_concepts = [
|
|
2469
2490
|
c for c in self.concepts.values() if c.address in concrete_addresses
|
|
2470
2491
|
]
|
|
2492
|
+
for concept in self.concepts.values():
|
|
2493
|
+
if concept.derivation == PurposeLineage.MERGE:
|
|
2494
|
+
ms = concept.lineage
|
|
2495
|
+
assert isinstance(ms, MergeStatement)
|
|
2496
|
+
for parent in ms.concepts:
|
|
2497
|
+
self.merged_concepts[parent.address] = concept
|
|
2471
2498
|
|
|
2472
2499
|
def validate_concept(self, lookup: str, meta: Meta | None = None):
|
|
2473
2500
|
existing: Concept = self.concepts.get(lookup) # type: ignore
|
|
@@ -2503,7 +2530,7 @@ class Environment(BaseModel):
|
|
|
2503
2530
|
self.concepts[f"{alias}.{key}"] = concept.with_namespace(alias)
|
|
2504
2531
|
for key, datasource in environment.datasources.items():
|
|
2505
2532
|
self.datasources[f"{alias}.{key}"] = datasource.with_namespace(alias)
|
|
2506
|
-
self.
|
|
2533
|
+
self.gen_concept_list_caches()
|
|
2507
2534
|
return self
|
|
2508
2535
|
|
|
2509
2536
|
def add_file_import(self, path: str, alias: str, env: Environment | None = None):
|
|
@@ -2602,7 +2629,7 @@ class Environment(BaseModel):
|
|
|
2602
2629
|
from trilogy.core.environment_helpers import generate_related_concepts
|
|
2603
2630
|
|
|
2604
2631
|
generate_related_concepts(concept, self)
|
|
2605
|
-
self.
|
|
2632
|
+
self.gen_concept_list_caches()
|
|
2606
2633
|
return concept
|
|
2607
2634
|
|
|
2608
2635
|
def add_datasource(
|
|
@@ -2612,12 +2639,12 @@ class Environment(BaseModel):
|
|
|
2612
2639
|
):
|
|
2613
2640
|
if not datasource.namespace or datasource.namespace == DEFAULT_NAMESPACE:
|
|
2614
2641
|
self.datasources[datasource.name] = datasource
|
|
2615
|
-
self.
|
|
2642
|
+
self.gen_concept_list_caches()
|
|
2616
2643
|
return datasource
|
|
2617
2644
|
self.datasources[datasource.namespace + "." + datasource.identifier] = (
|
|
2618
2645
|
datasource
|
|
2619
2646
|
)
|
|
2620
|
-
self.
|
|
2647
|
+
self.gen_concept_list_caches()
|
|
2621
2648
|
return datasource
|
|
2622
2649
|
|
|
2623
2650
|
|
|
@@ -136,6 +136,6 @@ def optimize_ctes(
|
|
|
136
136
|
select.where_clause.conditional if select.where_clause else None
|
|
137
137
|
)
|
|
138
138
|
root_cte.requires_nesting = False
|
|
139
|
-
sort_select_output(
|
|
139
|
+
sort_select_output(root_cte, select)
|
|
140
140
|
|
|
141
141
|
return filter_irrelevant_ctes(input, root_cte)
|
{pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/concept_strategies_v3.py
RENAMED
|
@@ -337,7 +337,9 @@ def generate_node(
|
|
|
337
337
|
|
|
338
338
|
|
|
339
339
|
def validate_stack(
|
|
340
|
-
stack: List[StrategyNode],
|
|
340
|
+
stack: List[StrategyNode],
|
|
341
|
+
concepts: List[Concept],
|
|
342
|
+
accept_partial: bool = False,
|
|
341
343
|
) -> tuple[ValidationResult, set[str], set[str], set[str]]:
|
|
342
344
|
found_map = defaultdict(set)
|
|
343
345
|
found_addresses: set[str] = set()
|
|
@@ -357,6 +359,7 @@ def validate_stack(
|
|
|
357
359
|
if accept_partial:
|
|
358
360
|
found_addresses.add(concept.address)
|
|
359
361
|
found_map[str(node)].add(concept)
|
|
362
|
+
# zip in those we know we found
|
|
360
363
|
if not all([c.address in found_addresses for c in concepts]):
|
|
361
364
|
return (
|
|
362
365
|
ValidationResult.INCOMPLETE,
|
|
@@ -388,7 +391,7 @@ def search_concepts(
|
|
|
388
391
|
hist = history.get_history(mandatory_list, accept_partial)
|
|
389
392
|
if hist is not False:
|
|
390
393
|
logger.info(
|
|
391
|
-
f"{depth_to_prefix(depth)}{LOGGER_PREFIX} Returning search node from history"
|
|
394
|
+
f"{depth_to_prefix(depth)}{LOGGER_PREFIX} Returning search node from history for {[c.address for c in mandatory_list]} with accept_partial {accept_partial}"
|
|
392
395
|
)
|
|
393
396
|
assert not isinstance(hist, bool)
|
|
394
397
|
return hist
|
|
@@ -454,7 +457,6 @@ def _search_concepts(
|
|
|
454
457
|
if node:
|
|
455
458
|
stack.append(node)
|
|
456
459
|
node.resolve()
|
|
457
|
-
found.add(priority_concept.address)
|
|
458
460
|
# these concepts should not be attempted to be sourced again
|
|
459
461
|
# as fetching them requires operating on a subset of concepts
|
|
460
462
|
if priority_concept.derivation in [
|
|
@@ -473,11 +475,12 @@ def _search_concepts(
|
|
|
473
475
|
complete, found, missing, partial = validate_stack(
|
|
474
476
|
stack, mandatory_list, accept_partial
|
|
475
477
|
)
|
|
476
|
-
|
|
478
|
+
|
|
477
479
|
logger.info(
|
|
478
480
|
f"{depth_to_prefix(depth)}{LOGGER_PREFIX} finished concept loop for {priority_concept} flag for accepting partial addresses is "
|
|
479
481
|
f" {accept_partial} (complete: {complete}), have {found} from {[n for n in stack]} (missing {missing} partial {partial}), attempted {attempted}"
|
|
480
482
|
)
|
|
483
|
+
# early exit if we have a complete stack with one node
|
|
481
484
|
# we can only early exit if we have a complete stack
|
|
482
485
|
# and we are not looking for more non-partial sources
|
|
483
486
|
if complete == ValidationResult.COMPLETE and (
|
|
@@ -514,6 +517,10 @@ def _search_concepts(
|
|
|
514
517
|
parents=stack,
|
|
515
518
|
depth=depth,
|
|
516
519
|
partial_concepts=all_partial,
|
|
520
|
+
# always hide merge concepts
|
|
521
|
+
hidden_concepts=[
|
|
522
|
+
x for x in mandatory_list if x.derivation == PurposeLineage.MERGE
|
|
523
|
+
],
|
|
517
524
|
)
|
|
518
525
|
|
|
519
526
|
# ensure we can resolve our final merge
|
{pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/__init__.py
RENAMED
|
@@ -5,10 +5,10 @@ from .group_to_node import gen_group_to_node
|
|
|
5
5
|
from .basic_node import gen_basic_node
|
|
6
6
|
from .select_node import gen_select_node
|
|
7
7
|
from .unnest_node import gen_unnest_node
|
|
8
|
-
from .
|
|
8
|
+
from .node_merge_node import gen_merge_node
|
|
9
9
|
from .rowset_node import gen_rowset_node
|
|
10
10
|
from .multiselect_node import gen_multiselect_node
|
|
11
|
-
from .
|
|
11
|
+
from .concept_merge_node import gen_concept_merge_node
|
|
12
12
|
|
|
13
13
|
__all__ = [
|
|
14
14
|
"gen_filter_node",
|
{pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/common.py
RENAMED
|
@@ -196,6 +196,7 @@ def gen_enrichment_node(
|
|
|
196
196
|
log_lambda(
|
|
197
197
|
f"{str(type(base_node).__name__)} returning merge node with group node + enrichment node"
|
|
198
198
|
)
|
|
199
|
+
|
|
199
200
|
return MergeNode(
|
|
200
201
|
input_concepts=unique(
|
|
201
202
|
join_keys + extra_required + base_node.output_concepts, "address"
|
|
@@ -7,27 +7,30 @@ from trilogy.core.processing.nodes import MergeNode, NodeJoin, History
|
|
|
7
7
|
from trilogy.core.processing.nodes.base_node import concept_list_to_grain, StrategyNode
|
|
8
8
|
from typing import List
|
|
9
9
|
|
|
10
|
-
from trilogy.core.enums import JoinType
|
|
10
|
+
from trilogy.core.enums import JoinType, PurposeLineage
|
|
11
11
|
from trilogy.constants import logger
|
|
12
12
|
from trilogy.core.processing.utility import padding
|
|
13
13
|
from trilogy.core.processing.node_generators.common import concept_to_relevant_joins
|
|
14
14
|
from itertools import combinations
|
|
15
15
|
from trilogy.core.processing.node_generators.common import resolve_join_order
|
|
16
|
+
from trilogy.utility import unique
|
|
17
|
+
|
|
16
18
|
|
|
17
19
|
LOGGER_PREFIX = "[GEN_CONCEPT_MERGE_NODE]"
|
|
18
20
|
|
|
19
21
|
|
|
20
|
-
def merge_joins(
|
|
22
|
+
def merge_joins(
|
|
23
|
+
parents: List[StrategyNode], merge_concepts: List[Concept]
|
|
24
|
+
) -> List[NodeJoin]:
|
|
21
25
|
output = []
|
|
22
26
|
for left, right in combinations(parents, 2):
|
|
23
27
|
output.append(
|
|
24
28
|
NodeJoin(
|
|
25
29
|
left_node=left,
|
|
26
30
|
right_node=right,
|
|
27
|
-
concepts=
|
|
28
|
-
base.merge_concept,
|
|
29
|
-
],
|
|
31
|
+
concepts=merge_concepts,
|
|
30
32
|
join_type=JoinType.FULL,
|
|
33
|
+
filter_to_mutual=True,
|
|
31
34
|
)
|
|
32
35
|
)
|
|
33
36
|
return resolve_join_order(output)
|
|
@@ -50,37 +53,70 @@ def gen_concept_merge_node(
|
|
|
50
53
|
lineage: MergeStatement = concept.lineage
|
|
51
54
|
|
|
52
55
|
base_parents: List[StrategyNode] = []
|
|
56
|
+
|
|
57
|
+
# get additional concepts that should be merged across the environments
|
|
58
|
+
additional_merge: List[Concept] = [*lineage.concepts]
|
|
59
|
+
for x in local_optional:
|
|
60
|
+
if x.address in environment.merged_concepts:
|
|
61
|
+
ms = environment.merged_concepts[x.address].lineage
|
|
62
|
+
assert isinstance(ms, MergeStatement)
|
|
63
|
+
additional_merge += ms.concepts
|
|
64
|
+
|
|
53
65
|
for select in lineage.concepts:
|
|
54
66
|
# if it's a merge concept, filter it out of the optional
|
|
55
67
|
sub_optional = [
|
|
56
68
|
x
|
|
57
69
|
for x in local_optional
|
|
58
|
-
if x.address not in
|
|
70
|
+
if x.address not in environment.merged_concepts
|
|
71
|
+
and x.namespace == select.namespace
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
sub_additional_merge = [
|
|
75
|
+
x for x in additional_merge if x.namespace == select.namespace
|
|
59
76
|
]
|
|
77
|
+
sub_optional += sub_additional_merge
|
|
78
|
+
final: List[Concept] = unique([select] + sub_optional, "address")
|
|
60
79
|
snode: StrategyNode = source_concepts(
|
|
61
|
-
mandatory_list=
|
|
80
|
+
mandatory_list=final,
|
|
62
81
|
environment=environment,
|
|
63
82
|
g=g,
|
|
64
83
|
depth=depth + 1,
|
|
65
84
|
history=history,
|
|
66
85
|
)
|
|
86
|
+
|
|
67
87
|
if not snode:
|
|
68
88
|
logger.info(
|
|
69
89
|
f"{padding(depth)}{LOGGER_PREFIX} Cannot generate merge node for {concept}"
|
|
70
90
|
)
|
|
71
91
|
return None
|
|
72
|
-
|
|
92
|
+
for x in sub_additional_merge:
|
|
93
|
+
snode.add_output_concept(environment.merged_concepts[x.address])
|
|
73
94
|
base_parents.append(snode)
|
|
74
95
|
|
|
75
|
-
node_joins = merge_joins(
|
|
96
|
+
node_joins = merge_joins(
|
|
97
|
+
base_parents,
|
|
98
|
+
unique(
|
|
99
|
+
[environment.merged_concepts[x.address] for x in additional_merge],
|
|
100
|
+
"address",
|
|
101
|
+
),
|
|
102
|
+
)
|
|
76
103
|
|
|
77
104
|
enrichment = set([x.address for x in local_optional])
|
|
78
|
-
outputs = [
|
|
105
|
+
outputs = [
|
|
106
|
+
x
|
|
107
|
+
for y in base_parents
|
|
108
|
+
for x in y.output_concepts
|
|
109
|
+
if x.derivation != PurposeLineage.MERGE
|
|
110
|
+
]
|
|
79
111
|
|
|
80
112
|
additional_relevant = [x for x in outputs if x.address in enrichment]
|
|
113
|
+
final_outputs = outputs + additional_relevant + [concept]
|
|
81
114
|
node = MergeNode(
|
|
82
115
|
input_concepts=[x for y in base_parents for x in y.output_concepts],
|
|
83
|
-
output_concepts=
|
|
116
|
+
output_concepts=[x for x in final_outputs],
|
|
117
|
+
hidden_concepts=[
|
|
118
|
+
x for x in final_outputs if x.derivation == PurposeLineage.MERGE
|
|
119
|
+
],
|
|
84
120
|
environment=environment,
|
|
85
121
|
g=g,
|
|
86
122
|
depth=depth,
|
|
@@ -127,9 +163,19 @@ def gen_concept_merge_node(
|
|
|
127
163
|
)
|
|
128
164
|
return node
|
|
129
165
|
|
|
166
|
+
# we still need the hidden concepts to be returned to the search
|
|
167
|
+
# since they must be on the final node
|
|
168
|
+
# to avoid further recursion
|
|
169
|
+
# TODO: let the downstream search know they were found
|
|
130
170
|
return MergeNode(
|
|
131
171
|
input_concepts=enrich_node.output_concepts + node.output_concepts,
|
|
172
|
+
# also filter out the
|
|
132
173
|
output_concepts=node.output_concepts + local_optional,
|
|
174
|
+
hidden_concepts=[
|
|
175
|
+
x
|
|
176
|
+
for x in node.output_concepts + local_optional
|
|
177
|
+
if x.derivation == PurposeLineage.MERGE
|
|
178
|
+
],
|
|
133
179
|
environment=environment,
|
|
134
180
|
g=g,
|
|
135
181
|
depth=depth,
|
{pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/rowset_node.py
RENAMED
|
@@ -10,7 +10,7 @@ from trilogy.core.processing.nodes import MergeNode, NodeJoin, History, Strategy
|
|
|
10
10
|
from trilogy.core.processing.nodes.base_node import concept_list_to_grain
|
|
11
11
|
from typing import List
|
|
12
12
|
|
|
13
|
-
from trilogy.core.enums import JoinType
|
|
13
|
+
from trilogy.core.enums import JoinType, PurposeLineage
|
|
14
14
|
from trilogy.constants import logger
|
|
15
15
|
from trilogy.core.processing.utility import padding
|
|
16
16
|
from trilogy.core.processing.node_generators.common import concept_to_relevant_joins
|
|
@@ -53,7 +53,7 @@ def gen_rowset_node(
|
|
|
53
53
|
return None
|
|
54
54
|
node.conditions = select.where_clause.conditional if select.where_clause else None
|
|
55
55
|
# rebuild any cached info with the new condition clause
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
enrichment = set([x.address for x in local_optional])
|
|
58
58
|
rowset_relevant = [
|
|
59
59
|
x
|
|
@@ -71,9 +71,15 @@ def gen_rowset_node(
|
|
|
71
71
|
if select.where_clause:
|
|
72
72
|
for item in additional_relevant:
|
|
73
73
|
node.partial_concepts.append(item)
|
|
74
|
-
|
|
74
|
+
node.hidden_concepts = [
|
|
75
|
+
x
|
|
76
|
+
for x in node.output_concepts
|
|
77
|
+
if x.address not in [y.address for y in local_optional + [concept]]
|
|
78
|
+
and x.derivation != PurposeLineage.ROWSET
|
|
79
|
+
]
|
|
75
80
|
# assume grain to be output of select
|
|
76
81
|
# but don't include anything aggregate at this point
|
|
82
|
+
node.rebuild_cache()
|
|
77
83
|
assert node.resolution_cache
|
|
78
84
|
node.resolution_cache.grain = concept_list_to_grain(
|
|
79
85
|
node.output_concepts, parent_sources=node.resolution_cache.datasources
|
|
@@ -27,12 +27,18 @@ class History(BaseModel):
|
|
|
27
27
|
self,
|
|
28
28
|
search: list[Concept],
|
|
29
29
|
accept_partial: bool = False,
|
|
30
|
+
parent_key: str = "",
|
|
30
31
|
) -> StrategyNode | None | bool:
|
|
32
|
+
key = self._concepts_to_lookup(
|
|
33
|
+
search,
|
|
34
|
+
accept_partial,
|
|
35
|
+
)
|
|
36
|
+
if parent_key and parent_key == key:
|
|
37
|
+
raise ValueError(
|
|
38
|
+
f"Parent key {parent_key} is the same as the current key {key}"
|
|
39
|
+
)
|
|
31
40
|
return self.history.get(
|
|
32
|
-
|
|
33
|
-
search,
|
|
34
|
-
accept_partial,
|
|
35
|
-
),
|
|
41
|
+
key,
|
|
36
42
|
False,
|
|
37
43
|
)
|
|
38
44
|
|
|
@@ -107,6 +107,7 @@ class StrategyNode:
|
|
|
107
107
|
conditions: Conditional | Comparison | Parenthetical | None = None,
|
|
108
108
|
force_group: bool | None = None,
|
|
109
109
|
grain: Optional[Grain] = None,
|
|
110
|
+
hidden_concepts: List[Concept] | None = None,
|
|
110
111
|
):
|
|
111
112
|
self.input_concepts: List[Concept] = (
|
|
112
113
|
unique(input_concepts, "address") if input_concepts else []
|
|
@@ -129,6 +130,7 @@ class StrategyNode:
|
|
|
129
130
|
self.grain = grain
|
|
130
131
|
self.force_group = force_group
|
|
131
132
|
self.tainted = False
|
|
133
|
+
self.hidden_concepts = hidden_concepts or []
|
|
132
134
|
for parent in self.parents:
|
|
133
135
|
if not parent:
|
|
134
136
|
raise SyntaxError("Unresolvable parent")
|
|
@@ -182,6 +184,7 @@ class StrategyNode:
|
|
|
182
184
|
|
|
183
185
|
def rebuild_cache(self) -> QueryDatasource:
|
|
184
186
|
self.tainted = True
|
|
187
|
+
self.output_lcl = LooseConceptList(concepts=self.output_concepts)
|
|
185
188
|
if not self.resolution_cache:
|
|
186
189
|
return self.resolve()
|
|
187
190
|
self.resolution_cache = None
|
|
@@ -104,6 +104,7 @@ class MergeNode(StrategyNode):
|
|
|
104
104
|
depth: int = 0,
|
|
105
105
|
grain: Grain | None = None,
|
|
106
106
|
conditions: Conditional | None = None,
|
|
107
|
+
hidden_concepts: List[Concept] | None = None,
|
|
107
108
|
):
|
|
108
109
|
super().__init__(
|
|
109
110
|
input_concepts=input_concepts,
|
|
@@ -117,6 +118,7 @@ class MergeNode(StrategyNode):
|
|
|
117
118
|
force_group=force_group,
|
|
118
119
|
grain=grain,
|
|
119
120
|
conditions=conditions,
|
|
121
|
+
hidden_concepts=hidden_concepts,
|
|
120
122
|
)
|
|
121
123
|
self.join_concepts = join_concepts
|
|
122
124
|
self.force_join_type = force_join_type
|
|
@@ -205,6 +207,7 @@ class MergeNode(StrategyNode):
|
|
|
205
207
|
joins = self.translate_node_joins(final_joins)
|
|
206
208
|
else:
|
|
207
209
|
return []
|
|
210
|
+
|
|
208
211
|
for join in joins:
|
|
209
212
|
logger.info(
|
|
210
213
|
f"{self.logging_prefix}{LOGGER_PREFIX} final join {join.join_type} {[str(c) for c in join.concepts]}"
|
|
@@ -327,5 +330,6 @@ class MergeNode(StrategyNode):
|
|
|
327
330
|
partial_concepts=self.partial_concepts,
|
|
328
331
|
force_group=force_group,
|
|
329
332
|
condition=self.conditions,
|
|
333
|
+
hidden_concepts=self.hidden_concepts,
|
|
330
334
|
)
|
|
331
335
|
return qds
|
|
@@ -216,6 +216,7 @@ def datasource_to_ctes(
|
|
|
216
216
|
condition=query_datasource.condition,
|
|
217
217
|
partial_concepts=query_datasource.partial_concepts,
|
|
218
218
|
join_derived_concepts=query_datasource.join_derived_concepts,
|
|
219
|
+
hidden_concepts=query_datasource.hidden_concepts,
|
|
219
220
|
)
|
|
220
221
|
if cte.grain != query_datasource.grain:
|
|
221
222
|
raise ValueError("Grain was corrupted in CTE generation")
|
|
@@ -437,6 +437,7 @@ class BaseDialect:
|
|
|
437
437
|
self.render_concept_sql(c, cte)
|
|
438
438
|
for c in cte.output_columns
|
|
439
439
|
if c.address not in [y.address for y in cte.join_derived_concepts]
|
|
440
|
+
and c.address not in [y.address for y in cte.hidden_concepts]
|
|
440
441
|
] + [
|
|
441
442
|
f"{self.QUOTE_CHARACTER}{c.safe_address}{self.QUOTE_CHARACTER}"
|
|
442
443
|
for c in cte.join_derived_concepts
|
|
@@ -444,7 +445,9 @@ class BaseDialect:
|
|
|
444
445
|
else:
|
|
445
446
|
# otherwse, assume we are unnesting directly in the select
|
|
446
447
|
select_columns = [
|
|
447
|
-
self.render_concept_sql(c, cte)
|
|
448
|
+
self.render_concept_sql(c, cte)
|
|
449
|
+
for c in cte.output_columns
|
|
450
|
+
if c.address not in [y.address for y in cte.hidden_concepts]
|
|
448
451
|
]
|
|
449
452
|
return CompiledCTE(
|
|
450
453
|
name=cte.name,
|
|
@@ -100,22 +100,7 @@ class PrestoConfig(DialectConfig):
|
|
|
100
100
|
return f"presto://{self.username}:{self.password}@{self.host}:{self.port}/{self.catalog}"
|
|
101
101
|
|
|
102
102
|
|
|
103
|
-
class TrinoConfig(
|
|
104
|
-
def __init__(
|
|
105
|
-
self,
|
|
106
|
-
host: str,
|
|
107
|
-
port: int,
|
|
108
|
-
username: str,
|
|
109
|
-
password: str,
|
|
110
|
-
catalog: str,
|
|
111
|
-
schema: str | None = None,
|
|
112
|
-
):
|
|
113
|
-
self.host = host
|
|
114
|
-
self.port = port
|
|
115
|
-
self.username = username
|
|
116
|
-
self.password = password
|
|
117
|
-
self.catalog = catalog
|
|
118
|
-
self.schema = schema
|
|
103
|
+
class TrinoConfig(PrestoConfig):
|
|
119
104
|
|
|
120
105
|
def connection_string(self) -> str:
|
|
121
106
|
if self.schema:
|
|
@@ -27,6 +27,9 @@ FUNCTION_MAP = {
|
|
|
27
27
|
FunctionType.QUARTER: lambda x: f"EXTRACT(QUARTER from {x[0]})",
|
|
28
28
|
# math
|
|
29
29
|
FunctionType.DIVIDE: lambda x: f"SAFE_DIVIDE({x[0]},{x[1]})",
|
|
30
|
+
FunctionType.DATE_ADD: lambda x: f"DATE_ADD('{x[1]}', {x[2]}, {x[0]})",
|
|
31
|
+
FunctionType.CURRENT_DATE: lambda x: "CURRENT_DATE",
|
|
32
|
+
FunctionType.CURRENT_DATETIME: lambda x: "CURRENT_TIMESTAMP",
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
FUNCTION_GRAIN_MATCH_MAP = {
|
|
@@ -42,11 +45,10 @@ CREATE OR REPLACE TABLE {{ output.address }} AS
|
|
|
42
45
|
{% endif %}{%- if ctes %}
|
|
43
46
|
WITH {% for cte in ctes %}
|
|
44
47
|
{{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
|
|
45
|
-
|
|
46
|
-
SELECT
|
|
47
48
|
{%- if full_select -%}
|
|
48
49
|
{{full_select}}
|
|
49
|
-
{%- else
|
|
50
|
+
{%- else %}
|
|
51
|
+
SELECT
|
|
50
52
|
{%- for select in select_columns %}
|
|
51
53
|
{{ select }}{% if not loop.last %},{% endif %}{% endfor %}
|
|
52
54
|
{% if base %}FROM
|
|
@@ -981,13 +981,13 @@ class ParseToObjects(Transformer):
|
|
|
981
981
|
|
|
982
982
|
@v_args(meta=True)
|
|
983
983
|
def merge_statement(self, meta: Meta, args) -> MergeStatement:
|
|
984
|
-
|
|
985
984
|
parsed = [self.environment.concepts[x] for x in args]
|
|
986
985
|
datatypes = {x.datatype for x in parsed}
|
|
987
|
-
if not len(datatypes) == 1:
|
|
986
|
+
if not len(datatypes) == 1 and self.environment.concepts.fail_on_missing:
|
|
987
|
+
type_dict = {x.address: x.datatype for x in parsed}
|
|
988
988
|
raise SyntaxError(
|
|
989
|
-
f"Cannot merge concepts with different
|
|
990
|
-
f"line: {meta.line} concepts: {
|
|
989
|
+
f"Cannot merge concepts with different datatype"
|
|
990
|
+
f"line: {meta.line} concepts: {type_dict}"
|
|
991
991
|
)
|
|
992
992
|
merge = MergeStatement(concepts=parsed, datatype=datatypes.pop())
|
|
993
993
|
new = merge.merge_concept
|
|
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.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/basic_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/filter_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/group_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/group_to_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/select_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/trilogy/core/processing/node_generators/unnest_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.107 → pytrilogy-0.0.1.109}/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
|