pytrilogy 0.0.3.54__tar.gz → 0.0.3.56__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.3.54/pytrilogy.egg-info → pytrilogy-0.0.3.56}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/pytrilogy.egg-info/SOURCES.txt +7 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/constants.py +2 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/enums.py +6 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/functions.py +3 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/models/author.py +12 -4
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/models/execute.py +207 -2
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/optimization.py +3 -3
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/optimizations/inline_datasource.py +5 -7
- pytrilogy-0.0.3.56/trilogy/core/processing/concept_strategies_v3.py +592 -0
- pytrilogy-0.0.3.56/trilogy/core/processing/discovery_node_factory.py +469 -0
- pytrilogy-0.0.3.56/trilogy/core/processing/discovery_utility.py +123 -0
- pytrilogy-0.0.3.56/trilogy/core/processing/discovery_validation.py +155 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/__init__.py +2 -0
- pytrilogy-0.0.3.56/trilogy/core/processing/node_generators/recursive_node.py +87 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/select_node.py +6 -8
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/nodes/__init__.py +4 -4
- pytrilogy-0.0.3.56/trilogy/core/processing/nodes/recursive_node.py +46 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/query_processor.py +7 -1
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/dialect/base.py +11 -2
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/dialect/bigquery.py +5 -6
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/dialect/common.py +19 -3
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/dialect/duckdb.py +1 -1
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/dialect/snowflake.py +8 -8
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/parsing/common.py +4 -3
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/parsing/parse_engine.py +12 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/parsing/trilogy.lark +3 -1
- pytrilogy-0.0.3.56/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/std/date.preql +3 -1
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/std/geography.preql +4 -0
- pytrilogy-0.0.3.56/trilogy/std/money.preql +67 -0
- pytrilogy-0.0.3.56/trilogy/std/net.preql +8 -0
- pytrilogy-0.0.3.54/trilogy/core/processing/concept_strategies_v3.py +0 -1147
- pytrilogy-0.0.3.54/trilogy/std/money.preql +0 -6
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/README.md +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/setup.cfg +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/setup.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_failure.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_parsing_failures.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/authoring/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/models/build.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/models/environment.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/__init__.py +0 -0
- /pytrilogy-0.0.3.54/trilogy/core/processing/node_generators/select_helpers/__init__.py → /pytrilogy-0.0.3.56/trilogy/core/processing/discovery_loop.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.54/trilogy/core/statements → pytrilogy-0.0.3.56/trilogy/core/processing/node_generators/select_helpers}/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.54/trilogy/dialect → pytrilogy-0.0.3.56/trilogy/core/statements}/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/statements/author.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/core/utility.py +0 -0
- {pytrilogy-0.0.3.54/trilogy/metadata → pytrilogy-0.0.3.56/trilogy/dialect}/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.54/trilogy/parsing → pytrilogy-0.0.3.56/trilogy/metadata}/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.54/trilogy/scripts → pytrilogy-0.0.3.56/trilogy/parsing}/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.54/trilogy/std → pytrilogy-0.0.3.56/trilogy/scripts}/__init__.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.0.3.54 → pytrilogy-0.0.3.56}/trilogy/utility.py +0 -0
|
@@ -71,6 +71,10 @@ trilogy/core/optimizations/inline_datasource.py
|
|
|
71
71
|
trilogy/core/optimizations/predicate_pushdown.py
|
|
72
72
|
trilogy/core/processing/__init__.py
|
|
73
73
|
trilogy/core/processing/concept_strategies_v3.py
|
|
74
|
+
trilogy/core/processing/discovery_loop.py
|
|
75
|
+
trilogy/core/processing/discovery_node_factory.py
|
|
76
|
+
trilogy/core/processing/discovery_utility.py
|
|
77
|
+
trilogy/core/processing/discovery_validation.py
|
|
74
78
|
trilogy/core/processing/graph_utils.py
|
|
75
79
|
trilogy/core/processing/utility.py
|
|
76
80
|
trilogy/core/processing/node_generators/__init__.py
|
|
@@ -81,6 +85,7 @@ trilogy/core/processing/node_generators/group_node.py
|
|
|
81
85
|
trilogy/core/processing/node_generators/group_to_node.py
|
|
82
86
|
trilogy/core/processing/node_generators/multiselect_node.py
|
|
83
87
|
trilogy/core/processing/node_generators/node_merge_node.py
|
|
88
|
+
trilogy/core/processing/node_generators/recursive_node.py
|
|
84
89
|
trilogy/core/processing/node_generators/rowset_node.py
|
|
85
90
|
trilogy/core/processing/node_generators/select_merge_node.py
|
|
86
91
|
trilogy/core/processing/node_generators/select_node.py
|
|
@@ -95,6 +100,7 @@ trilogy/core/processing/nodes/base_node.py
|
|
|
95
100
|
trilogy/core/processing/nodes/filter_node.py
|
|
96
101
|
trilogy/core/processing/nodes/group_node.py
|
|
97
102
|
trilogy/core/processing/nodes/merge_node.py
|
|
103
|
+
trilogy/core/processing/nodes/recursive_node.py
|
|
98
104
|
trilogy/core/processing/nodes/select_node_v2.py
|
|
99
105
|
trilogy/core/processing/nodes/union_node.py
|
|
100
106
|
trilogy/core/processing/nodes/unnest_node.py
|
|
@@ -136,4 +142,5 @@ trilogy/std/date.preql
|
|
|
136
142
|
trilogy/std/display.preql
|
|
137
143
|
trilogy/std/geography.preql
|
|
138
144
|
trilogy/std/money.preql
|
|
145
|
+
trilogy/std/net.preql
|
|
139
146
|
trilogy/std/report.preql
|
|
@@ -42,6 +42,7 @@ class Purpose(Enum):
|
|
|
42
42
|
|
|
43
43
|
class Derivation(Enum):
|
|
44
44
|
BASIC = "basic"
|
|
45
|
+
GROUP_TO = "group_to"
|
|
45
46
|
WINDOW = "window"
|
|
46
47
|
AGGREGATE = "aggregate"
|
|
47
48
|
FILTER = "filter"
|
|
@@ -51,6 +52,7 @@ class Derivation(Enum):
|
|
|
51
52
|
ROOT = "root"
|
|
52
53
|
ROWSET = "rowset"
|
|
53
54
|
MULTISELECT = "multiselect"
|
|
55
|
+
RECURSIVE = "recursive"
|
|
54
56
|
|
|
55
57
|
|
|
56
58
|
class Granularity(Enum):
|
|
@@ -117,6 +119,7 @@ class FunctionType(Enum):
|
|
|
117
119
|
|
|
118
120
|
# structural
|
|
119
121
|
UNNEST = "unnest"
|
|
122
|
+
RECURSE_EDGE = "recurse_edge"
|
|
120
123
|
|
|
121
124
|
UNION = "union"
|
|
122
125
|
|
|
@@ -233,6 +236,8 @@ class FunctionClass(Enum):
|
|
|
233
236
|
|
|
234
237
|
ONE_TO_MANY = [FunctionType.UNNEST]
|
|
235
238
|
|
|
239
|
+
RECURSIVE = [FunctionType.RECURSE_EDGE]
|
|
240
|
+
|
|
236
241
|
|
|
237
242
|
class Boolean(Enum):
|
|
238
243
|
TRUE = "true"
|
|
@@ -333,6 +338,7 @@ class SourceType(Enum):
|
|
|
333
338
|
MERGE = "merge"
|
|
334
339
|
BASIC = "basic"
|
|
335
340
|
UNION = "union"
|
|
341
|
+
RECURSIVE = "recursive"
|
|
336
342
|
|
|
337
343
|
|
|
338
344
|
class ShowCategory(Enum):
|
|
@@ -190,6 +190,9 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
190
190
|
output_type_function=get_unnest_output_type,
|
|
191
191
|
arg_count=1,
|
|
192
192
|
),
|
|
193
|
+
FunctionType.RECURSE_EDGE: FunctionConfig(
|
|
194
|
+
arg_count=2,
|
|
195
|
+
),
|
|
193
196
|
FunctionType.GROUP: FunctionConfig(
|
|
194
197
|
arg_count=-1,
|
|
195
198
|
output_type_function=lambda args: get_output_type_at_index(args, 0),
|
|
@@ -1164,12 +1164,24 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1164
1164
|
and lineage.operator == FunctionType.UNNEST
|
|
1165
1165
|
):
|
|
1166
1166
|
return Derivation.UNNEST
|
|
1167
|
+
elif (
|
|
1168
|
+
lineage
|
|
1169
|
+
and isinstance(lineage, (BuildFunction, Function))
|
|
1170
|
+
and lineage.operator == FunctionType.RECURSE_EDGE
|
|
1171
|
+
):
|
|
1172
|
+
return Derivation.RECURSIVE
|
|
1167
1173
|
elif (
|
|
1168
1174
|
lineage
|
|
1169
1175
|
and isinstance(lineage, (BuildFunction, Function))
|
|
1170
1176
|
and lineage.operator == FunctionType.UNION
|
|
1171
1177
|
):
|
|
1172
1178
|
return Derivation.UNION
|
|
1179
|
+
elif (
|
|
1180
|
+
lineage
|
|
1181
|
+
and isinstance(lineage, (BuildFunction, Function))
|
|
1182
|
+
and lineage.operator == FunctionType.GROUP
|
|
1183
|
+
):
|
|
1184
|
+
return Derivation.GROUP_TO
|
|
1173
1185
|
elif (
|
|
1174
1186
|
lineage
|
|
1175
1187
|
and isinstance(lineage, (BuildFunction, Function))
|
|
@@ -1189,10 +1201,6 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1189
1201
|
return Derivation.CONSTANT
|
|
1190
1202
|
return Derivation.ROOT
|
|
1191
1203
|
|
|
1192
|
-
# @property
|
|
1193
|
-
# def derivation(self) -> Derivation:
|
|
1194
|
-
# return self.calculate_derivation(self.lineage, self.purpose)
|
|
1195
|
-
|
|
1196
1204
|
@classmethod
|
|
1197
1205
|
def calculate_granularity(cls, derivation: Derivation, grain: Grain, lineage):
|
|
1198
1206
|
from trilogy.core.models.build import BuildFunction
|
|
@@ -12,9 +12,16 @@ from pydantic import (
|
|
|
12
12
|
model_validator,
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
from trilogy.constants import
|
|
15
|
+
from trilogy.constants import (
|
|
16
|
+
CONFIG,
|
|
17
|
+
DEFAULT_NAMESPACE,
|
|
18
|
+
RECURSIVE_GATING_CONCEPT,
|
|
19
|
+
MagicConstants,
|
|
20
|
+
logger,
|
|
21
|
+
)
|
|
16
22
|
from trilogy.core.constants import CONSTANT_DATASET
|
|
17
23
|
from trilogy.core.enums import (
|
|
24
|
+
ComparisonOperator,
|
|
18
25
|
Derivation,
|
|
19
26
|
FunctionType,
|
|
20
27
|
Granularity,
|
|
@@ -24,16 +31,20 @@ from trilogy.core.enums import (
|
|
|
24
31
|
SourceType,
|
|
25
32
|
)
|
|
26
33
|
from trilogy.core.models.build import (
|
|
34
|
+
BuildCaseElse,
|
|
35
|
+
BuildCaseWhen,
|
|
27
36
|
BuildComparison,
|
|
28
37
|
BuildConcept,
|
|
29
38
|
BuildConditional,
|
|
30
39
|
BuildDatasource,
|
|
40
|
+
BuildExpr,
|
|
31
41
|
BuildFunction,
|
|
32
42
|
BuildGrain,
|
|
33
43
|
BuildOrderBy,
|
|
34
44
|
BuildParamaterizedConceptReference,
|
|
35
45
|
BuildParenthetical,
|
|
36
46
|
BuildRowsetItem,
|
|
47
|
+
DataType,
|
|
37
48
|
LooseBuildConceptList,
|
|
38
49
|
)
|
|
39
50
|
from trilogy.core.models.datasource import Address
|
|
@@ -841,6 +852,195 @@ class QueryDatasource(BaseModel):
|
|
|
841
852
|
return self.identifier
|
|
842
853
|
|
|
843
854
|
|
|
855
|
+
class AliasedExpression(BaseModel):
|
|
856
|
+
expr: BuildExpr
|
|
857
|
+
alias: str
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
class RecursiveCTE(CTE):
|
|
861
|
+
|
|
862
|
+
def generate_loop_functions(
|
|
863
|
+
self,
|
|
864
|
+
recursive_derived: BuildConcept,
|
|
865
|
+
left_recurse_concept: BuildConcept,
|
|
866
|
+
right_recurse_concept: BuildConcept,
|
|
867
|
+
) -> tuple[BuildConcept, BuildConcept, BuildConcept]:
|
|
868
|
+
|
|
869
|
+
join_gate = BuildConcept(
|
|
870
|
+
name=RECURSIVE_GATING_CONCEPT,
|
|
871
|
+
namespace=DEFAULT_NAMESPACE,
|
|
872
|
+
grain=recursive_derived.grain,
|
|
873
|
+
build_is_aggregate=False,
|
|
874
|
+
datatype=DataType.BOOL,
|
|
875
|
+
purpose=Purpose.KEY,
|
|
876
|
+
derivation=Derivation.BASIC,
|
|
877
|
+
lineage=BuildFunction(
|
|
878
|
+
operator=FunctionType.CASE,
|
|
879
|
+
arguments=[
|
|
880
|
+
BuildCaseWhen(
|
|
881
|
+
comparison=BuildComparison(
|
|
882
|
+
left=right_recurse_concept,
|
|
883
|
+
right=MagicConstants.NULL,
|
|
884
|
+
operator=ComparisonOperator.IS,
|
|
885
|
+
),
|
|
886
|
+
expr=True,
|
|
887
|
+
),
|
|
888
|
+
BuildCaseElse(expr=False),
|
|
889
|
+
],
|
|
890
|
+
output_datatype=DataType.BOOL,
|
|
891
|
+
output_purpose=Purpose.KEY,
|
|
892
|
+
),
|
|
893
|
+
)
|
|
894
|
+
bottom_join_gate = BuildConcept(
|
|
895
|
+
name=f"{RECURSIVE_GATING_CONCEPT}_two",
|
|
896
|
+
namespace=DEFAULT_NAMESPACE,
|
|
897
|
+
grain=recursive_derived.grain,
|
|
898
|
+
build_is_aggregate=False,
|
|
899
|
+
datatype=DataType.BOOL,
|
|
900
|
+
purpose=Purpose.KEY,
|
|
901
|
+
derivation=Derivation.BASIC,
|
|
902
|
+
lineage=BuildFunction(
|
|
903
|
+
operator=FunctionType.CASE,
|
|
904
|
+
arguments=[
|
|
905
|
+
BuildCaseWhen(
|
|
906
|
+
comparison=BuildComparison(
|
|
907
|
+
left=right_recurse_concept,
|
|
908
|
+
right=MagicConstants.NULL,
|
|
909
|
+
operator=ComparisonOperator.IS,
|
|
910
|
+
),
|
|
911
|
+
expr=True,
|
|
912
|
+
),
|
|
913
|
+
BuildCaseElse(expr=False),
|
|
914
|
+
],
|
|
915
|
+
output_datatype=DataType.BOOL,
|
|
916
|
+
output_purpose=Purpose.KEY,
|
|
917
|
+
),
|
|
918
|
+
)
|
|
919
|
+
bottom_derivation = BuildConcept(
|
|
920
|
+
name=recursive_derived.name + "_bottom",
|
|
921
|
+
namespace=recursive_derived.namespace,
|
|
922
|
+
grain=recursive_derived.grain,
|
|
923
|
+
build_is_aggregate=False,
|
|
924
|
+
datatype=recursive_derived.datatype,
|
|
925
|
+
purpose=recursive_derived.purpose,
|
|
926
|
+
derivation=Derivation.RECURSIVE,
|
|
927
|
+
lineage=BuildFunction(
|
|
928
|
+
operator=FunctionType.CASE,
|
|
929
|
+
arguments=[
|
|
930
|
+
BuildCaseWhen(
|
|
931
|
+
comparison=BuildComparison(
|
|
932
|
+
left=right_recurse_concept,
|
|
933
|
+
right=MagicConstants.NULL,
|
|
934
|
+
operator=ComparisonOperator.IS,
|
|
935
|
+
),
|
|
936
|
+
expr=recursive_derived,
|
|
937
|
+
),
|
|
938
|
+
BuildCaseElse(expr=right_recurse_concept),
|
|
939
|
+
],
|
|
940
|
+
output_datatype=recursive_derived.datatype,
|
|
941
|
+
output_purpose=recursive_derived.purpose,
|
|
942
|
+
),
|
|
943
|
+
)
|
|
944
|
+
return bottom_derivation, join_gate, bottom_join_gate
|
|
945
|
+
|
|
946
|
+
@property
|
|
947
|
+
def internal_ctes(self) -> List[CTE]:
|
|
948
|
+
filtered_output = [
|
|
949
|
+
x for x in self.output_columns if x.name != RECURSIVE_GATING_CONCEPT
|
|
950
|
+
]
|
|
951
|
+
recursive_derived = [
|
|
952
|
+
x for x in self.output_columns if x.derivation == Derivation.RECURSIVE
|
|
953
|
+
][0]
|
|
954
|
+
if not isinstance(recursive_derived.lineage, BuildFunction):
|
|
955
|
+
raise SyntaxError(
|
|
956
|
+
"Recursive CTEs must have a function lineage, found"
|
|
957
|
+
f" {recursive_derived.lineage}"
|
|
958
|
+
)
|
|
959
|
+
left_recurse_concept = recursive_derived.lineage.concept_arguments[0]
|
|
960
|
+
right_recurse_concept = recursive_derived.lineage.concept_arguments[1]
|
|
961
|
+
parent_ctes: List[CTE | UnionCTE]
|
|
962
|
+
if self.parent_ctes:
|
|
963
|
+
base = self.parent_ctes[0]
|
|
964
|
+
loop_input_cte = base
|
|
965
|
+
parent_ctes = [base]
|
|
966
|
+
parent_identifier = base.identifier
|
|
967
|
+
else:
|
|
968
|
+
raise SyntaxError("Recursive CTEs must have a parent CTE currently")
|
|
969
|
+
bottom_derivation, join_gate, bottom_join_gate = self.generate_loop_functions(
|
|
970
|
+
recursive_derived, left_recurse_concept, right_recurse_concept
|
|
971
|
+
)
|
|
972
|
+
base_output = [*filtered_output, join_gate]
|
|
973
|
+
bottom_output = []
|
|
974
|
+
for x in filtered_output:
|
|
975
|
+
if x.address == recursive_derived.address:
|
|
976
|
+
bottom_output.append(bottom_derivation)
|
|
977
|
+
else:
|
|
978
|
+
bottom_output.append(x)
|
|
979
|
+
|
|
980
|
+
bottom_output = [*bottom_output, bottom_join_gate]
|
|
981
|
+
top = CTE(
|
|
982
|
+
name=self.name,
|
|
983
|
+
source=self.source,
|
|
984
|
+
output_columns=base_output,
|
|
985
|
+
source_map=self.source_map,
|
|
986
|
+
grain=self.grain,
|
|
987
|
+
existence_source_map=self.existence_source_map,
|
|
988
|
+
parent_ctes=self.parent_ctes,
|
|
989
|
+
joins=self.joins,
|
|
990
|
+
condition=self.condition,
|
|
991
|
+
partial_concepts=self.partial_concepts,
|
|
992
|
+
hidden_concepts=self.hidden_concepts,
|
|
993
|
+
nullable_concepts=self.nullable_concepts,
|
|
994
|
+
join_derived_concepts=self.join_derived_concepts,
|
|
995
|
+
group_to_grain=self.group_to_grain,
|
|
996
|
+
order_by=self.order_by,
|
|
997
|
+
limit=self.limit,
|
|
998
|
+
)
|
|
999
|
+
top_cte_array: list[CTE | UnionCTE] = [top]
|
|
1000
|
+
bottom_source_map = {
|
|
1001
|
+
left_recurse_concept.address: [top.identifier],
|
|
1002
|
+
right_recurse_concept.address: [parent_identifier],
|
|
1003
|
+
# recursive_derived.address: self.source_map[recursive_derived.address],
|
|
1004
|
+
join_gate.address: [top.identifier],
|
|
1005
|
+
recursive_derived.address: [top.identifier],
|
|
1006
|
+
}
|
|
1007
|
+
bottom = CTE(
|
|
1008
|
+
name=self.name,
|
|
1009
|
+
source=self.source,
|
|
1010
|
+
output_columns=bottom_output,
|
|
1011
|
+
source_map=bottom_source_map,
|
|
1012
|
+
grain=self.grain,
|
|
1013
|
+
existence_source_map=self.existence_source_map,
|
|
1014
|
+
parent_ctes=top_cte_array + parent_ctes,
|
|
1015
|
+
joins=[
|
|
1016
|
+
Join(
|
|
1017
|
+
right_cte=loop_input_cte,
|
|
1018
|
+
jointype=JoinType.INNER,
|
|
1019
|
+
joinkey_pairs=[
|
|
1020
|
+
CTEConceptPair(
|
|
1021
|
+
left=recursive_derived,
|
|
1022
|
+
right=left_recurse_concept,
|
|
1023
|
+
existing_datasource=loop_input_cte.source,
|
|
1024
|
+
modifiers=[],
|
|
1025
|
+
cte=top,
|
|
1026
|
+
)
|
|
1027
|
+
],
|
|
1028
|
+
condition=BuildComparison(
|
|
1029
|
+
left=join_gate, right=True, operator=ComparisonOperator.IS_NOT
|
|
1030
|
+
),
|
|
1031
|
+
)
|
|
1032
|
+
],
|
|
1033
|
+
partial_concepts=self.partial_concepts,
|
|
1034
|
+
hidden_concepts=self.hidden_concepts,
|
|
1035
|
+
nullable_concepts=self.nullable_concepts,
|
|
1036
|
+
join_derived_concepts=self.join_derived_concepts,
|
|
1037
|
+
group_to_grain=self.group_to_grain,
|
|
1038
|
+
order_by=self.order_by,
|
|
1039
|
+
limit=self.limit,
|
|
1040
|
+
)
|
|
1041
|
+
return [top, bottom]
|
|
1042
|
+
|
|
1043
|
+
|
|
844
1044
|
class UnionCTE(BaseModel):
|
|
845
1045
|
name: str
|
|
846
1046
|
source: QueryDatasource
|
|
@@ -891,6 +1091,10 @@ class UnionCTE(BaseModel):
|
|
|
891
1091
|
def condition(self, value):
|
|
892
1092
|
raise NotImplementedError
|
|
893
1093
|
|
|
1094
|
+
@property
|
|
1095
|
+
def identifier(self) -> str:
|
|
1096
|
+
return self.name
|
|
1097
|
+
|
|
894
1098
|
@property
|
|
895
1099
|
def safe_identifier(self):
|
|
896
1100
|
return self.name
|
|
@@ -906,12 +1110,13 @@ class UnionCTE(BaseModel):
|
|
|
906
1110
|
|
|
907
1111
|
|
|
908
1112
|
class Join(BaseModel):
|
|
909
|
-
right_cte: CTE
|
|
1113
|
+
right_cte: CTE | UnionCTE
|
|
910
1114
|
jointype: JoinType
|
|
911
1115
|
left_cte: CTE | None = None
|
|
912
1116
|
joinkey_pairs: List[CTEConceptPair] | None = None
|
|
913
1117
|
inlined_ctes: set[str] = Field(default_factory=set)
|
|
914
1118
|
quote: str | None = None
|
|
1119
|
+
condition: BuildConditional | BuildComparison | BuildParenthetical | None = None
|
|
915
1120
|
|
|
916
1121
|
def inline_cte(self, cte: CTE):
|
|
917
1122
|
self.inlined_ctes.add(cte.name)
|
|
@@ -3,7 +3,7 @@ from trilogy.core.enums import BooleanOperator, Derivation
|
|
|
3
3
|
from trilogy.core.models.build import (
|
|
4
4
|
BuildConditional,
|
|
5
5
|
)
|
|
6
|
-
from trilogy.core.models.execute import CTE, UnionCTE
|
|
6
|
+
from trilogy.core.models.execute import CTE, RecursiveCTE, UnionCTE
|
|
7
7
|
from trilogy.core.optimizations import (
|
|
8
8
|
InlineDatasource,
|
|
9
9
|
OptimizationRule,
|
|
@@ -123,7 +123,7 @@ def gen_inverse_map(input: list[CTE | UnionCTE]) -> dict[str, list[CTE | UnionCT
|
|
|
123
123
|
SENSITIVE_DERIVATIONS = [
|
|
124
124
|
Derivation.UNNEST,
|
|
125
125
|
Derivation.WINDOW,
|
|
126
|
-
|
|
126
|
+
Derivation.RECURSIVE,
|
|
127
127
|
]
|
|
128
128
|
|
|
129
129
|
|
|
@@ -133,7 +133,7 @@ def is_direct_return_eligible(cte: CTE | UnionCTE) -> CTE | UnionCTE | None:
|
|
|
133
133
|
if len(cte.parent_ctes) != 1:
|
|
134
134
|
return None
|
|
135
135
|
direct_parent = cte.parent_ctes[0]
|
|
136
|
-
if isinstance(direct_parent, UnionCTE):
|
|
136
|
+
if isinstance(direct_parent, (UnionCTE, RecursiveCTE)):
|
|
137
137
|
return None
|
|
138
138
|
|
|
139
139
|
output_addresses = set([x.address for x in cte.output_columns])
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
from collections import defaultdict
|
|
2
2
|
|
|
3
3
|
from trilogy.constants import CONFIG
|
|
4
|
-
|
|
5
|
-
# from trilogy.core.models.datasource import Datasource
|
|
6
4
|
from trilogy.core.models.build import BuildDatasource
|
|
7
|
-
from trilogy.core.models.execute import
|
|
8
|
-
CTE,
|
|
9
|
-
UnionCTE,
|
|
10
|
-
)
|
|
5
|
+
from trilogy.core.models.execute import CTE, RecursiveCTE, UnionCTE
|
|
11
6
|
from trilogy.core.optimizations.base_optimization import OptimizationRule
|
|
12
7
|
|
|
13
8
|
|
|
@@ -24,7 +19,8 @@ class InlineDatasource(OptimizationRule):
|
|
|
24
19
|
return any(
|
|
25
20
|
self.optimize(x, inverse_map=inverse_map) for x in cte.internal_ctes
|
|
26
21
|
)
|
|
27
|
-
|
|
22
|
+
if isinstance(cte, RecursiveCTE):
|
|
23
|
+
return False
|
|
28
24
|
if not cte.parent_ctes:
|
|
29
25
|
return False
|
|
30
26
|
|
|
@@ -36,6 +32,8 @@ class InlineDatasource(OptimizationRule):
|
|
|
36
32
|
for parent_cte in cte.parent_ctes:
|
|
37
33
|
if isinstance(parent_cte, UnionCTE):
|
|
38
34
|
continue
|
|
35
|
+
if isinstance(parent_cte, RecursiveCTE):
|
|
36
|
+
continue
|
|
39
37
|
if not parent_cte.is_root_datasource:
|
|
40
38
|
self.debug(f"Cannot inline: parent {parent_cte.name} is not root")
|
|
41
39
|
continue
|