pytrilogy 0.0.3.113__tar.gz → 0.0.3.116__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.113/pytrilogy.egg-info → pytrilogy-0.0.3.116}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_functions.py +28 -1
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_parse_engine.py +14 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/constants.py +29 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/enums.py +6 -1
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/functions.py +33 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/models/author.py +126 -2
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/models/build.py +70 -7
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/models/environment.py +2 -1
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/optimization.py +3 -2
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/optimizations/hide_unused_concept.py +1 -5
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/concept_strategies_v3.py +26 -5
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/discovery_node_factory.py +2 -2
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/discovery_utility.py +11 -4
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/basic_node.py +26 -15
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/common.py +4 -1
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/filter_node.py +7 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/multiselect_node.py +3 -3
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/unnest_node.py +77 -6
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/statements/author.py +4 -1
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/base.py +42 -2
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/executor.py +1 -1
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/parsing/common.py +117 -20
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/parsing/parse_engine.py +115 -5
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/parsing/render.py +2 -1
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/parsing/trilogy.lark +20 -7
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/README.md +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/pytrilogy.egg-info/SOURCES.txt +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/requirements.txt +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/setup.cfg +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_execute_models.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_failure.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_parsing_failures.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_validators.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/constants.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/conversation.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/enums.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/execute.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/models.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/prompts.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/providers/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/providers/anthropic.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/providers/base.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/providers/google.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/providers/openai.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/ai/providers/utils.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/authoring/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/models/execute.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/discovery_validation.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/constant_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/recursive_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/nodes/recursive_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/utility.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/validation/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/validation/common.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/validation/concept.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/validation/datasource.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/validation/environment.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/validation/fix.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/metadata.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/std/color.preql +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/std/date.preql +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/std/geography.preql +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/std/metric.preql +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/std/money.preql +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/std/net.preql +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/std/ranking.preql +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/utility.py +0 -0
|
@@ -168,7 +168,7 @@ def test_date_functions(test_environment):
|
|
|
168
168
|
|
|
169
169
|
for dialect in TEST_DIALECTS:
|
|
170
170
|
engine = None
|
|
171
|
-
if isinstance(dialect,
|
|
171
|
+
if isinstance(dialect, DuckDBDialect):
|
|
172
172
|
engine = Dialects.DUCK_DB.default_executor(environment=test_environment)
|
|
173
173
|
dialect.compile_statement(process_query(test_environment, select))
|
|
174
174
|
if engine:
|
|
@@ -308,16 +308,43 @@ def test_string_functions(test_environment):
|
|
|
308
308
|
property regex_replace <- regexp_replace(category_name, 'a', 'b');
|
|
309
309
|
|
|
310
310
|
select
|
|
311
|
+
category_id,
|
|
311
312
|
test_name,
|
|
312
313
|
upper_name,
|
|
313
314
|
lower_name,
|
|
314
315
|
substring_name,
|
|
315
316
|
strpos_name,
|
|
317
|
+
like_name,
|
|
318
|
+
like_alt,
|
|
319
|
+
regex_contains,
|
|
320
|
+
regex_substring,
|
|
321
|
+
regex_replace,
|
|
322
|
+
hash(category_name, md5) -> hash_md5,
|
|
323
|
+
hash(category_name, sha1) -> hash_sha1,
|
|
324
|
+
hash(category_name, sha256) -> hash_sha256,
|
|
325
|
+
# hash(category_name, sha512) -> hash_sha512
|
|
316
326
|
;"""
|
|
317
327
|
env, parsed = parse(declarations, environment=test_environment)
|
|
318
328
|
select: SelectStatement = parsed[-1]
|
|
319
329
|
for dialect in TEST_DIALECTS:
|
|
330
|
+
engine = None
|
|
331
|
+
if isinstance(dialect, DuckDBDialect):
|
|
332
|
+
engine = Dialects.DUCK_DB.default_executor(environment=test_environment)
|
|
320
333
|
dialect.compile_statement(process_query(test_environment, select))
|
|
334
|
+
if engine:
|
|
335
|
+
engine.execute_raw_sql(
|
|
336
|
+
"""CREATE TABLE tblCategory AS
|
|
337
|
+
SELECT
|
|
338
|
+
2 category_id,
|
|
339
|
+
'category_a' as category_name
|
|
340
|
+
UNION ALL
|
|
341
|
+
SELECT
|
|
342
|
+
3 category_id,
|
|
343
|
+
'category_b' as category_name;"""
|
|
344
|
+
)
|
|
345
|
+
results = engine.execute_query(select)
|
|
346
|
+
assert results
|
|
347
|
+
assert results.fetchall()
|
|
321
348
|
|
|
322
349
|
|
|
323
350
|
def test_case_function(test_environment):
|
|
@@ -121,3 +121,17 @@ def test_alias_error():
|
|
|
121
121
|
with raises(InvalidSyntaxException) as e:
|
|
122
122
|
env.parse(TEXT2)
|
|
123
123
|
assert ERROR_CODES[201] in str(e.value), e.value
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_semicolon_error():
|
|
127
|
+
env = Environment()
|
|
128
|
+
TEXT2 = """
|
|
129
|
+
const a <- 1;
|
|
130
|
+
|
|
131
|
+
select
|
|
132
|
+
a+2 as fun,
|
|
133
|
+
|
|
134
|
+
"""
|
|
135
|
+
with raises(InvalidSyntaxException) as e:
|
|
136
|
+
env.parse(TEXT2)
|
|
137
|
+
assert ERROR_CODES[202] in str(e.value), e.value
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import random
|
|
2
|
+
from contextlib import contextmanager
|
|
2
3
|
from dataclasses import dataclass, field
|
|
3
4
|
from enum import Enum
|
|
4
5
|
from logging import getLogger
|
|
6
|
+
from typing import Any
|
|
5
7
|
|
|
6
8
|
logger = getLogger("trilogy")
|
|
7
9
|
|
|
@@ -29,6 +31,7 @@ class Optimizations:
|
|
|
29
31
|
constant_inlining: bool = True
|
|
30
32
|
constant_inline_cutoff: int = 10
|
|
31
33
|
direct_return: bool = True
|
|
34
|
+
hide_unused_concepts: bool = True
|
|
32
35
|
|
|
33
36
|
|
|
34
37
|
@dataclass
|
|
@@ -50,6 +53,32 @@ class Rendering:
|
|
|
50
53
|
parameters: bool = True
|
|
51
54
|
concise: bool = False
|
|
52
55
|
|
|
56
|
+
@contextmanager
|
|
57
|
+
def temporary(self, **kwargs: Any):
|
|
58
|
+
"""
|
|
59
|
+
Context manager to temporarily set attributes and revert them afterwards.
|
|
60
|
+
|
|
61
|
+
Usage:
|
|
62
|
+
r = Rendering()
|
|
63
|
+
with r.temporary(parameters=False, concise=True):
|
|
64
|
+
# parameters is False, concise is True here
|
|
65
|
+
do_something()
|
|
66
|
+
# parameters and concise are back to their original values
|
|
67
|
+
"""
|
|
68
|
+
# Store original values
|
|
69
|
+
original_values = {key: getattr(self, key) for key in kwargs}
|
|
70
|
+
|
|
71
|
+
# Set new values
|
|
72
|
+
for key, value in kwargs.items():
|
|
73
|
+
setattr(self, key, value)
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
yield self
|
|
77
|
+
finally:
|
|
78
|
+
# Restore original values
|
|
79
|
+
for key, value in original_values.items():
|
|
80
|
+
setattr(self, key, value)
|
|
81
|
+
|
|
53
82
|
|
|
54
83
|
@dataclass
|
|
55
84
|
class Parsing:
|
|
@@ -169,6 +169,7 @@ class FunctionType(Enum):
|
|
|
169
169
|
ARRAY_SORT = "array_sort"
|
|
170
170
|
ARRAY_TRANSFORM = "array_transform"
|
|
171
171
|
ARRAY_TO_STRING = "array_to_string"
|
|
172
|
+
ARRAY_FILTER = "array_filter"
|
|
172
173
|
|
|
173
174
|
# MAP
|
|
174
175
|
MAP_KEYS = "map_keys"
|
|
@@ -204,6 +205,7 @@ class FunctionType(Enum):
|
|
|
204
205
|
MIN = "min"
|
|
205
206
|
AVG = "avg"
|
|
206
207
|
ARRAY_AGG = "array_agg"
|
|
208
|
+
ANY = "any"
|
|
207
209
|
|
|
208
210
|
# String
|
|
209
211
|
LIKE = "like"
|
|
@@ -215,6 +217,7 @@ class FunctionType(Enum):
|
|
|
215
217
|
CONTAINS = "contains"
|
|
216
218
|
TRIM = "trim"
|
|
217
219
|
REPLACE = "replace"
|
|
220
|
+
HASH = "hash"
|
|
218
221
|
|
|
219
222
|
# STRING REGEX
|
|
220
223
|
REGEXP_CONTAINS = "regexp_contains"
|
|
@@ -244,6 +247,7 @@ class FunctionType(Enum):
|
|
|
244
247
|
DATE_ADD = "date_add"
|
|
245
248
|
DATE_SUB = "date_sub"
|
|
246
249
|
DATE_DIFF = "date_diff"
|
|
250
|
+
DATE_SPINE = "date_spine"
|
|
247
251
|
|
|
248
252
|
# UNIX
|
|
249
253
|
UNIX_TO_TIMESTAMP = "unix_to_timestamp"
|
|
@@ -263,6 +267,7 @@ class FunctionClass(Enum):
|
|
|
263
267
|
FunctionType.ARRAY_AGG,
|
|
264
268
|
FunctionType.COUNT,
|
|
265
269
|
FunctionType.COUNT_DISTINCT,
|
|
270
|
+
FunctionType.ANY,
|
|
266
271
|
]
|
|
267
272
|
SINGLE_ROW = [
|
|
268
273
|
FunctionType.CONSTANT,
|
|
@@ -270,7 +275,7 @@ class FunctionClass(Enum):
|
|
|
270
275
|
FunctionType.CURRENT_DATETIME,
|
|
271
276
|
]
|
|
272
277
|
|
|
273
|
-
ONE_TO_MANY = [FunctionType.UNNEST]
|
|
278
|
+
ONE_TO_MANY = [FunctionType.UNNEST, FunctionType.DATE_SPINE]
|
|
274
279
|
|
|
275
280
|
RECURSIVE = [FunctionType.RECURSE_EDGE]
|
|
276
281
|
|
|
@@ -212,6 +212,14 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
212
212
|
output_type_function=get_unnest_output_type,
|
|
213
213
|
arg_count=1,
|
|
214
214
|
),
|
|
215
|
+
FunctionType.DATE_SPINE: FunctionConfig(
|
|
216
|
+
valid_inputs={
|
|
217
|
+
DataType.DATE,
|
|
218
|
+
},
|
|
219
|
+
output_purpose=Purpose.KEY,
|
|
220
|
+
output_type=DataType.DATE,
|
|
221
|
+
arg_count=2,
|
|
222
|
+
),
|
|
215
223
|
FunctionType.RECURSE_EDGE: FunctionConfig(
|
|
216
224
|
arg_count=2,
|
|
217
225
|
),
|
|
@@ -318,6 +326,18 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
318
326
|
output_type_function=get_transform_output_type,
|
|
319
327
|
arg_count=3,
|
|
320
328
|
),
|
|
329
|
+
FunctionType.ARRAY_FILTER: FunctionConfig(
|
|
330
|
+
valid_inputs=[
|
|
331
|
+
{
|
|
332
|
+
DataType.ARRAY,
|
|
333
|
+
},
|
|
334
|
+
{*DataType},
|
|
335
|
+
{*DataType},
|
|
336
|
+
],
|
|
337
|
+
output_purpose=Purpose.PROPERTY,
|
|
338
|
+
output_type_function=get_transform_output_type,
|
|
339
|
+
arg_count=3,
|
|
340
|
+
),
|
|
321
341
|
FunctionType.ARRAY_TO_STRING: FunctionConfig(
|
|
322
342
|
valid_inputs={
|
|
323
343
|
DataType.ARRAY,
|
|
@@ -887,6 +907,11 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
887
907
|
),
|
|
888
908
|
arg_count=1,
|
|
889
909
|
),
|
|
910
|
+
FunctionType.ANY: FunctionConfig(
|
|
911
|
+
valid_inputs={*DataType},
|
|
912
|
+
output_purpose=Purpose.PROPERTY,
|
|
913
|
+
arg_count=1,
|
|
914
|
+
),
|
|
890
915
|
FunctionType.AVG: FunctionConfig(
|
|
891
916
|
valid_inputs={
|
|
892
917
|
DataType.INTEGER,
|
|
@@ -903,6 +928,14 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
903
928
|
output_type=DataType.TIMESTAMP,
|
|
904
929
|
arg_count=1,
|
|
905
930
|
),
|
|
931
|
+
FunctionType.HASH: FunctionConfig(
|
|
932
|
+
valid_inputs={
|
|
933
|
+
DataType.STRING,
|
|
934
|
+
},
|
|
935
|
+
output_purpose=Purpose.PROPERTY,
|
|
936
|
+
output_type=DataType.STRING,
|
|
937
|
+
arg_count=2,
|
|
938
|
+
),
|
|
906
939
|
}
|
|
907
940
|
|
|
908
941
|
EXCLUDED_FUNCTIONS = {
|
|
@@ -865,6 +865,7 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
865
865
|
AggregateWrapper,
|
|
866
866
|
RowsetItem,
|
|
867
867
|
MultiSelectLineage,
|
|
868
|
+
Comparison,
|
|
868
869
|
]
|
|
869
870
|
] = None
|
|
870
871
|
namespace: str = Field(default=DEFAULT_NAMESPACE, validate_default=True)
|
|
@@ -1079,6 +1080,7 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1079
1080
|
| AggregateWrapper
|
|
1080
1081
|
| RowsetItem
|
|
1081
1082
|
| MultiSelectLineage
|
|
1083
|
+
| Comparison
|
|
1082
1084
|
| None,
|
|
1083
1085
|
Grain,
|
|
1084
1086
|
set[str] | None,
|
|
@@ -1178,6 +1180,7 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1178
1180
|
AggregateWrapper,
|
|
1179
1181
|
RowsetItem,
|
|
1180
1182
|
MultiSelectLineage,
|
|
1183
|
+
Comparison,
|
|
1181
1184
|
],
|
|
1182
1185
|
output: List[ConceptRef],
|
|
1183
1186
|
):
|
|
@@ -1204,6 +1207,7 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1204
1207
|
def calculate_derivation(self, lineage, purpose: Purpose) -> Derivation:
|
|
1205
1208
|
from trilogy.core.models.build import (
|
|
1206
1209
|
BuildAggregateWrapper,
|
|
1210
|
+
BuildComparison,
|
|
1207
1211
|
BuildFilterItem,
|
|
1208
1212
|
BuildFunction,
|
|
1209
1213
|
BuildMultiSelectLineage,
|
|
@@ -1221,6 +1225,8 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1221
1225
|
# return Derivation.PARENTHETICAL
|
|
1222
1226
|
elif lineage and isinstance(lineage, (BuildRowsetItem, RowsetItem)):
|
|
1223
1227
|
return Derivation.ROWSET
|
|
1228
|
+
elif lineage and isinstance(lineage, BuildComparison):
|
|
1229
|
+
return Derivation.BASIC
|
|
1224
1230
|
elif lineage and isinstance(
|
|
1225
1231
|
lineage, (BuildMultiSelectLineage, MultiSelectLineage)
|
|
1226
1232
|
):
|
|
@@ -1234,7 +1240,7 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1234
1240
|
elif (
|
|
1235
1241
|
lineage
|
|
1236
1242
|
and isinstance(lineage, (BuildFunction, Function))
|
|
1237
|
-
and lineage.operator
|
|
1243
|
+
and lineage.operator in FunctionClass.ONE_TO_MANY.value
|
|
1238
1244
|
):
|
|
1239
1245
|
return Derivation.UNNEST
|
|
1240
1246
|
elif (
|
|
@@ -1286,7 +1292,8 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1286
1292
|
elif (
|
|
1287
1293
|
lineage
|
|
1288
1294
|
and isinstance(lineage, (Function, BuildFunction))
|
|
1289
|
-
and lineage.operator
|
|
1295
|
+
and lineage.operator
|
|
1296
|
+
in (FunctionType.UNNEST, FunctionType.UNION, FunctionType.DATE_SPINE)
|
|
1290
1297
|
):
|
|
1291
1298
|
return Granularity.MULTI_ROW
|
|
1292
1299
|
elif lineage and all(
|
|
@@ -2132,6 +2139,89 @@ class AlignClause(Namespaced, BaseModel):
|
|
|
2132
2139
|
)
|
|
2133
2140
|
|
|
2134
2141
|
|
|
2142
|
+
class DeriveItem(Namespaced, DataTyped, ConceptArgs, Mergeable, BaseModel):
|
|
2143
|
+
expr: Expr
|
|
2144
|
+
name: str
|
|
2145
|
+
namespace: str
|
|
2146
|
+
|
|
2147
|
+
@property
|
|
2148
|
+
def derived_concept(self) -> str:
|
|
2149
|
+
return f"{self.namespace}.{self.name}"
|
|
2150
|
+
# return ConceptRef(
|
|
2151
|
+
# address=f"{self.namespace}.{self.name}",
|
|
2152
|
+
# datatype=arg_to_datatype(self.expr),
|
|
2153
|
+
# )
|
|
2154
|
+
|
|
2155
|
+
def with_namespace(self, namespace):
|
|
2156
|
+
return DeriveItem.model_construct(
|
|
2157
|
+
expr=(self.expr.with_namespace(namespace) if self.expr else None),
|
|
2158
|
+
name=self.name,
|
|
2159
|
+
namespace=namespace,
|
|
2160
|
+
)
|
|
2161
|
+
|
|
2162
|
+
def with_merge(
|
|
2163
|
+
self, source: Concept, target: Concept, modifiers: List[Modifier]
|
|
2164
|
+
) -> "DeriveItem":
|
|
2165
|
+
return DeriveItem.model_construct(
|
|
2166
|
+
expr=(
|
|
2167
|
+
self.expr.with_merge(source, target, modifiers)
|
|
2168
|
+
if isinstance(self.expr, Mergeable)
|
|
2169
|
+
else self.expr
|
|
2170
|
+
),
|
|
2171
|
+
name=self.name,
|
|
2172
|
+
namespace=self.namespace,
|
|
2173
|
+
)
|
|
2174
|
+
|
|
2175
|
+
def with_reference_replacement(self, source, target):
|
|
2176
|
+
return DeriveItem.model_construct(
|
|
2177
|
+
expr=(
|
|
2178
|
+
self.expr.with_reference_replacement(source, target)
|
|
2179
|
+
if isinstance(self.expr, Mergeable)
|
|
2180
|
+
else self.expr
|
|
2181
|
+
),
|
|
2182
|
+
name=self.name,
|
|
2183
|
+
namespace=self.namespace,
|
|
2184
|
+
)
|
|
2185
|
+
|
|
2186
|
+
|
|
2187
|
+
class DeriveClause(Mergeable, Namespaced, BaseModel):
|
|
2188
|
+
items: List[DeriveItem]
|
|
2189
|
+
|
|
2190
|
+
def with_namespace(self, namespace: str) -> "DeriveClause":
|
|
2191
|
+
return DeriveClause.model_construct(
|
|
2192
|
+
items=[
|
|
2193
|
+
x.with_namespace(namespace) if isinstance(x, Namespaced) else x
|
|
2194
|
+
for x in self.items
|
|
2195
|
+
]
|
|
2196
|
+
)
|
|
2197
|
+
|
|
2198
|
+
def with_merge(
|
|
2199
|
+
self, source: Concept, target: Concept, modifiers: List[Modifier]
|
|
2200
|
+
) -> "DeriveClause":
|
|
2201
|
+
return DeriveClause.model_construct(
|
|
2202
|
+
items=[
|
|
2203
|
+
(
|
|
2204
|
+
x.with_merge(source, target, modifiers)
|
|
2205
|
+
if isinstance(x, Mergeable)
|
|
2206
|
+
else x
|
|
2207
|
+
)
|
|
2208
|
+
for x in self.items
|
|
2209
|
+
]
|
|
2210
|
+
)
|
|
2211
|
+
|
|
2212
|
+
def with_reference_replacement(self, source, target):
|
|
2213
|
+
return DeriveClause.model_construct(
|
|
2214
|
+
items=[
|
|
2215
|
+
(
|
|
2216
|
+
x.with_reference_replacement(source, target)
|
|
2217
|
+
if isinstance(x, Mergeable)
|
|
2218
|
+
else x
|
|
2219
|
+
)
|
|
2220
|
+
for x in self.items
|
|
2221
|
+
]
|
|
2222
|
+
)
|
|
2223
|
+
|
|
2224
|
+
|
|
2135
2225
|
class SelectLineage(Mergeable, Namespaced, BaseModel):
|
|
2136
2226
|
selection: List[ConceptRef]
|
|
2137
2227
|
hidden_components: set[str]
|
|
@@ -2176,15 +2266,40 @@ class SelectLineage(Mergeable, Namespaced, BaseModel):
|
|
|
2176
2266
|
),
|
|
2177
2267
|
)
|
|
2178
2268
|
|
|
2269
|
+
def with_namespace(self, namespace):
|
|
2270
|
+
return SelectLineage.model_construct(
|
|
2271
|
+
selection=[x.with_namespace(namespace) for x in self.selection],
|
|
2272
|
+
hidden_components=self.hidden_components,
|
|
2273
|
+
local_concepts={
|
|
2274
|
+
x: y.with_namespace(namespace) for x, y in self.local_concepts.items()
|
|
2275
|
+
},
|
|
2276
|
+
order_by=self.order_by.with_namespace(namespace) if self.order_by else None,
|
|
2277
|
+
limit=self.limit,
|
|
2278
|
+
meta=self.meta,
|
|
2279
|
+
grain=self.grain.with_namespace(namespace),
|
|
2280
|
+
where_clause=(
|
|
2281
|
+
self.where_clause.with_namespace(namespace)
|
|
2282
|
+
if self.where_clause
|
|
2283
|
+
else None
|
|
2284
|
+
),
|
|
2285
|
+
having_clause=(
|
|
2286
|
+
self.having_clause.with_namespace(namespace)
|
|
2287
|
+
if self.having_clause
|
|
2288
|
+
else None
|
|
2289
|
+
),
|
|
2290
|
+
)
|
|
2291
|
+
|
|
2179
2292
|
|
|
2180
2293
|
class MultiSelectLineage(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
2181
2294
|
selects: List[SelectLineage]
|
|
2182
2295
|
align: AlignClause
|
|
2296
|
+
|
|
2183
2297
|
namespace: str
|
|
2184
2298
|
order_by: Optional[OrderBy] = None
|
|
2185
2299
|
limit: Optional[int] = None
|
|
2186
2300
|
where_clause: Union["WhereClause", None] = Field(default=None)
|
|
2187
2301
|
having_clause: Union["HavingClause", None] = Field(default=None)
|
|
2302
|
+
derive: DeriveClause | None = None
|
|
2188
2303
|
hidden_components: set[str]
|
|
2189
2304
|
|
|
2190
2305
|
@property
|
|
@@ -2210,6 +2325,11 @@ class MultiSelectLineage(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
2210
2325
|
new = MultiSelectLineage.model_construct(
|
|
2211
2326
|
selects=[s.with_merge(source, target, modifiers) for s in self.selects],
|
|
2212
2327
|
align=self.align,
|
|
2328
|
+
derive=(
|
|
2329
|
+
self.derive.with_merge(source, target, modifiers)
|
|
2330
|
+
if self.derive
|
|
2331
|
+
else None
|
|
2332
|
+
),
|
|
2213
2333
|
namespace=self.namespace,
|
|
2214
2334
|
hidden_components=self.hidden_components,
|
|
2215
2335
|
order_by=(
|
|
@@ -2235,6 +2355,7 @@ class MultiSelectLineage(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
2235
2355
|
return MultiSelectLineage.model_construct(
|
|
2236
2356
|
selects=[c.with_namespace(namespace) for c in self.selects],
|
|
2237
2357
|
align=self.align.with_namespace(namespace),
|
|
2358
|
+
derive=self.derive.with_namespace(namespace) if self.derive else None,
|
|
2238
2359
|
namespace=namespace,
|
|
2239
2360
|
hidden_components=self.hidden_components,
|
|
2240
2361
|
order_by=self.order_by.with_namespace(namespace) if self.order_by else None,
|
|
@@ -2256,6 +2377,9 @@ class MultiSelectLineage(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
2256
2377
|
output = set()
|
|
2257
2378
|
for item in self.align.items:
|
|
2258
2379
|
output.add(item.aligned_concept)
|
|
2380
|
+
if self.derive:
|
|
2381
|
+
for ditem in self.derive.items:
|
|
2382
|
+
output.add(ditem.derived_concept)
|
|
2259
2383
|
return output
|
|
2260
2384
|
|
|
2261
2385
|
@property
|
|
@@ -48,6 +48,8 @@ from trilogy.core.models.author import (
|
|
|
48
48
|
Concept,
|
|
49
49
|
ConceptRef,
|
|
50
50
|
Conditional,
|
|
51
|
+
DeriveClause,
|
|
52
|
+
DeriveItem,
|
|
51
53
|
FilterItem,
|
|
52
54
|
FuncArgs,
|
|
53
55
|
Function,
|
|
@@ -134,8 +136,9 @@ def concept_is_relevant(
|
|
|
134
136
|
if concept.purpose in (Purpose.METRIC,):
|
|
135
137
|
if all([c in others for c in concept.grain.components]):
|
|
136
138
|
return False
|
|
139
|
+
if concept.derivation in (Derivation.UNNEST,):
|
|
140
|
+
return True
|
|
137
141
|
if concept.derivation in (Derivation.BASIC,):
|
|
138
|
-
|
|
139
142
|
return any(concept_is_relevant(c, others) for c in concept.concept_arguments)
|
|
140
143
|
if concept.granularity == Granularity.SINGLE_ROW:
|
|
141
144
|
return False
|
|
@@ -246,7 +249,7 @@ class BuildParamaterizedConceptReference(DataTyped):
|
|
|
246
249
|
concept: BuildConcept
|
|
247
250
|
|
|
248
251
|
def __str__(self):
|
|
249
|
-
return f":{self.concept.address}"
|
|
252
|
+
return f":{self.concept.address.replace('.', '_')}"
|
|
250
253
|
|
|
251
254
|
@property
|
|
252
255
|
def safe_address(self) -> str:
|
|
@@ -1267,6 +1270,22 @@ class BuildAlignClause:
|
|
|
1267
1270
|
items: List[BuildAlignItem]
|
|
1268
1271
|
|
|
1269
1272
|
|
|
1273
|
+
@dataclass
|
|
1274
|
+
class BuildDeriveClause:
|
|
1275
|
+
items: List[BuildDeriveItem]
|
|
1276
|
+
|
|
1277
|
+
|
|
1278
|
+
@dataclass
|
|
1279
|
+
class BuildDeriveItem:
|
|
1280
|
+
expr: BuildExpr
|
|
1281
|
+
name: str
|
|
1282
|
+
namespace: str = field(default=DEFAULT_NAMESPACE)
|
|
1283
|
+
|
|
1284
|
+
@property
|
|
1285
|
+
def address(self) -> str:
|
|
1286
|
+
return f"{self.namespace}.{self.name}"
|
|
1287
|
+
|
|
1288
|
+
|
|
1270
1289
|
@dataclass
|
|
1271
1290
|
class BuildSelectLineage:
|
|
1272
1291
|
selection: List[BuildConcept]
|
|
@@ -1298,12 +1317,16 @@ class BuildMultiSelectLineage(BuildConceptArgs):
|
|
|
1298
1317
|
limit: Optional[int] = None
|
|
1299
1318
|
where_clause: Union["BuildWhereClause", None] = field(default=None)
|
|
1300
1319
|
having_clause: Union["BuildHavingClause", None] = field(default=None)
|
|
1320
|
+
derive: BuildDeriveClause | None = None
|
|
1301
1321
|
|
|
1302
1322
|
@property
|
|
1303
1323
|
def derived_concepts(self) -> set[str]:
|
|
1304
1324
|
output = set()
|
|
1305
1325
|
for item in self.align.items:
|
|
1306
1326
|
output.add(item.aligned_concept)
|
|
1327
|
+
if self.derive:
|
|
1328
|
+
for ditem in self.derive.items:
|
|
1329
|
+
output.add(ditem.address)
|
|
1307
1330
|
return output
|
|
1308
1331
|
|
|
1309
1332
|
@property
|
|
@@ -1311,10 +1334,12 @@ class BuildMultiSelectLineage(BuildConceptArgs):
|
|
|
1311
1334
|
return self.build_output_components
|
|
1312
1335
|
|
|
1313
1336
|
@property
|
|
1314
|
-
def
|
|
1315
|
-
output = set()
|
|
1316
|
-
|
|
1317
|
-
output
|
|
1337
|
+
def calculated_derivations(self) -> set[str]:
|
|
1338
|
+
output: set[str] = set()
|
|
1339
|
+
if not self.derive:
|
|
1340
|
+
return output
|
|
1341
|
+
for item in self.derive.items:
|
|
1342
|
+
output.add(item.address)
|
|
1318
1343
|
return output
|
|
1319
1344
|
|
|
1320
1345
|
@property
|
|
@@ -1334,6 +1359,7 @@ class BuildMultiSelectLineage(BuildConceptArgs):
|
|
|
1334
1359
|
for c in x.concepts:
|
|
1335
1360
|
if c.address in cte.output_lcl:
|
|
1336
1361
|
return c
|
|
1362
|
+
|
|
1337
1363
|
raise SyntaxError(
|
|
1338
1364
|
f"Could not find upstream map for multiselect {str(concept)} on cte ({cte})"
|
|
1339
1365
|
)
|
|
@@ -1668,7 +1694,6 @@ class Factory:
|
|
|
1668
1694
|
valid_inputs=base.valid_inputs,
|
|
1669
1695
|
arg_count=base.arg_count,
|
|
1670
1696
|
)
|
|
1671
|
-
|
|
1672
1697
|
new = BuildFunction(
|
|
1673
1698
|
operator=base.operator,
|
|
1674
1699
|
arguments=[self.handle_constant(self.build(c)) for c in raw_args],
|
|
@@ -1724,6 +1749,14 @@ class Factory:
|
|
|
1724
1749
|
return self._build_concept(base)
|
|
1725
1750
|
|
|
1726
1751
|
def _build_concept(self, base: Concept) -> BuildConcept:
|
|
1752
|
+
try:
|
|
1753
|
+
return self.__build_concept(base)
|
|
1754
|
+
except RecursionError as e:
|
|
1755
|
+
raise RecursionError(
|
|
1756
|
+
f"Recursion error building concept {base.address}. This is likely due to a circular reference."
|
|
1757
|
+
) from e
|
|
1758
|
+
|
|
1759
|
+
def __build_concept(self, base: Concept) -> BuildConcept:
|
|
1727
1760
|
# TODO: if we are using parameters, wrap it in a new model and use that in rendering
|
|
1728
1761
|
if base.address in self.local_concepts:
|
|
1729
1762
|
return self.local_concepts[base.address]
|
|
@@ -1953,6 +1986,28 @@ class Factory:
|
|
|
1953
1986
|
def _build_align_clause(self, base: AlignClause) -> BuildAlignClause:
|
|
1954
1987
|
return BuildAlignClause(items=[self._build_align_item(x) for x in base.items])
|
|
1955
1988
|
|
|
1989
|
+
@build.register
|
|
1990
|
+
def _(self, base: DeriveItem) -> BuildDeriveItem:
|
|
1991
|
+
return self._build_derive_item(base)
|
|
1992
|
+
|
|
1993
|
+
def _build_derive_item(self, base: DeriveItem) -> BuildDeriveItem:
|
|
1994
|
+
expr: Concept | FuncArgs = base.expr
|
|
1995
|
+
validation = requires_concept_nesting(expr)
|
|
1996
|
+
if validation:
|
|
1997
|
+
expr, _ = self.instantiate_concept(validation)
|
|
1998
|
+
return BuildDeriveItem(
|
|
1999
|
+
expr=self.build(expr),
|
|
2000
|
+
name=base.name,
|
|
2001
|
+
namespace=base.namespace,
|
|
2002
|
+
)
|
|
2003
|
+
|
|
2004
|
+
@build.register
|
|
2005
|
+
def _(self, base: DeriveClause) -> BuildDeriveClause:
|
|
2006
|
+
return self._build_derive_clause(base)
|
|
2007
|
+
|
|
2008
|
+
def _build_derive_clause(self, base: DeriveClause) -> BuildDeriveClause:
|
|
2009
|
+
return BuildDeriveClause(items=[self.build(x) for x in base.items])
|
|
2010
|
+
|
|
1956
2011
|
@build.register
|
|
1957
2012
|
def _(self, base: RowsetItem) -> BuildRowsetItem:
|
|
1958
2013
|
return self._build_rowset_item(base)
|
|
@@ -2002,6 +2057,13 @@ class Factory:
|
|
|
2002
2057
|
def _build_tuple_wrapper(self, base: TupleWrapper) -> TupleWrapper:
|
|
2003
2058
|
return TupleWrapper(val=[self.build(x) for x in base.val], type=base.type)
|
|
2004
2059
|
|
|
2060
|
+
@build.register
|
|
2061
|
+
def _(self, base: ListWrapper) -> ListWrapper:
|
|
2062
|
+
return self._build_list_wrapper(base)
|
|
2063
|
+
|
|
2064
|
+
def _build_list_wrapper(self, base: ListWrapper) -> ListWrapper:
|
|
2065
|
+
return ListWrapper([self.build(x) for x in base], type=base.type)
|
|
2066
|
+
|
|
2005
2067
|
@build.register
|
|
2006
2068
|
def _(self, base: FilterItem) -> BuildFilterItem:
|
|
2007
2069
|
return self._build_filter_item(base)
|
|
@@ -2147,6 +2209,7 @@ class Factory:
|
|
|
2147
2209
|
selects=base.selects,
|
|
2148
2210
|
grain=final_grain,
|
|
2149
2211
|
align=factory.build(base.align),
|
|
2212
|
+
derive=factory.build(base.derive) if base.derive else None,
|
|
2150
2213
|
# self.align.with_select_context(
|
|
2151
2214
|
# local_build_cache, self.grain, environment
|
|
2152
2215
|
# ),
|
|
@@ -413,7 +413,8 @@ class Environment(BaseModel):
|
|
|
413
413
|
self.imports[alias].append(imp_stm)
|
|
414
414
|
# we can't exit early
|
|
415
415
|
# as there may be new concepts
|
|
416
|
-
|
|
416
|
+
iteration: list[tuple[str, Concept]] = list(source.concepts.items())
|
|
417
|
+
for k, concept in iteration:
|
|
417
418
|
# skip internal namespace
|
|
418
419
|
if INTERNAL_NAMESPACE in concept.address:
|
|
419
420
|
continue
|
|
@@ -228,7 +228,8 @@ def optimize_ctes(
|
|
|
228
228
|
REGISTERED_RULES.append(PredicatePushdown())
|
|
229
229
|
if CONFIG.optimizations.predicate_pushdown:
|
|
230
230
|
REGISTERED_RULES.append(PredicatePushdownRemove())
|
|
231
|
-
|
|
231
|
+
if CONFIG.optimizations.hide_unused_concepts:
|
|
232
|
+
REGISTERED_RULES.append(HideUnusedConcepts())
|
|
232
233
|
for rule in REGISTERED_RULES:
|
|
233
234
|
loops = 0
|
|
234
235
|
complete = False
|
|
@@ -242,7 +243,7 @@ def optimize_ctes(
|
|
|
242
243
|
actions_taken = actions_taken or opt
|
|
243
244
|
complete = not actions_taken
|
|
244
245
|
loops += 1
|
|
245
|
-
|
|
246
|
+
input = reorder_ctes(filter_irrelevant_ctes(input, root_cte))
|
|
246
247
|
logger.info(
|
|
247
248
|
f"[Optimization] Finished checking for {type(rule).__name__} after {loops} loop(s)"
|
|
248
249
|
)
|
{pytrilogy-0.0.3.113 → pytrilogy-0.0.3.116}/trilogy/core/optimizations/hide_unused_concept.py
RENAMED
|
@@ -39,11 +39,7 @@ class HideUnusedConcepts(OptimizationRule):
|
|
|
39
39
|
self.log(
|
|
40
40
|
f"Hiding unused concepts {[x.address for x in add_to_hidden]} from {cte.name} (used: {used}, all: {[x.address for x in cte.output_columns]})"
|
|
41
41
|
)
|
|
42
|
-
candidates = [
|
|
43
|
-
x.address
|
|
44
|
-
for x in cte.output_columns
|
|
45
|
-
if x.address not in used and x.address not in cte.hidden_concepts
|
|
46
|
-
]
|
|
42
|
+
candidates = [x.address for x in cte.output_columns if x.address not in used]
|
|
47
43
|
if len(candidates) == len(set([x.address for x in cte.output_columns])):
|
|
48
44
|
# pop one out
|
|
49
45
|
candidates.pop()
|