pytrilogy 0.0.3.115__tar.gz → 0.0.3.117__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.115/pytrilogy.egg-info → pytrilogy-0.0.3.117}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_functions.py +28 -1
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/constants.py +1 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/enums.py +1 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/functions.py +8 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/models/author.py +123 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/models/build.py +53 -5
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/models/environment.py +2 -1
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/optimization.py +3 -2
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/optimizations/hide_unused_concept.py +1 -5
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/concept_strategies_v3.py +2 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/basic_node.py +26 -15
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/common.py +4 -1
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/multiselect_node.py +3 -3
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/statements/author.py +3 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/base.py +30 -2
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/executor.py +1 -1
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/parsing/common.py +109 -17
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/parsing/parse_engine.py +80 -11
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/parsing/trilogy.lark +11 -3
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/README.md +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/pytrilogy.egg-info/SOURCES.txt +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/requirements.txt +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/setup.cfg +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_execute_models.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_failure.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_parsing_failures.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_validators.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/constants.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/conversation.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/enums.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/execute.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/models.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/prompts.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/providers/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/providers/anthropic.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/providers/base.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/providers/google.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/providers/openai.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/ai/providers/utils.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/authoring/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/models/execute.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/discovery_node_factory.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/discovery_utility.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/discovery_validation.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/constant_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/recursive_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/nodes/recursive_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/utility.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/validation/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/validation/common.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/validation/concept.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/validation/datasource.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/validation/environment.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/validation/fix.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/metadata.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/std/color.preql +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/std/date.preql +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/std/geography.preql +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/std/metric.preql +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/std/money.preql +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/std/net.preql +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/std/ranking.preql +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/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):
|
|
@@ -928,6 +928,14 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
928
928
|
output_type=DataType.TIMESTAMP,
|
|
929
929
|
arg_count=1,
|
|
930
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
|
+
),
|
|
931
939
|
}
|
|
932
940
|
|
|
933
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
|
):
|
|
@@ -2133,6 +2139,89 @@ class AlignClause(Namespaced, BaseModel):
|
|
|
2133
2139
|
)
|
|
2134
2140
|
|
|
2135
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
|
+
|
|
2136
2225
|
class SelectLineage(Mergeable, Namespaced, BaseModel):
|
|
2137
2226
|
selection: List[ConceptRef]
|
|
2138
2227
|
hidden_components: set[str]
|
|
@@ -2177,15 +2266,40 @@ class SelectLineage(Mergeable, Namespaced, BaseModel):
|
|
|
2177
2266
|
),
|
|
2178
2267
|
)
|
|
2179
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
|
+
|
|
2180
2292
|
|
|
2181
2293
|
class MultiSelectLineage(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
2182
2294
|
selects: List[SelectLineage]
|
|
2183
2295
|
align: AlignClause
|
|
2296
|
+
|
|
2184
2297
|
namespace: str
|
|
2185
2298
|
order_by: Optional[OrderBy] = None
|
|
2186
2299
|
limit: Optional[int] = None
|
|
2187
2300
|
where_clause: Union["WhereClause", None] = Field(default=None)
|
|
2188
2301
|
having_clause: Union["HavingClause", None] = Field(default=None)
|
|
2302
|
+
derive: DeriveClause | None = None
|
|
2189
2303
|
hidden_components: set[str]
|
|
2190
2304
|
|
|
2191
2305
|
@property
|
|
@@ -2211,6 +2325,11 @@ class MultiSelectLineage(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
2211
2325
|
new = MultiSelectLineage.model_construct(
|
|
2212
2326
|
selects=[s.with_merge(source, target, modifiers) for s in self.selects],
|
|
2213
2327
|
align=self.align,
|
|
2328
|
+
derive=(
|
|
2329
|
+
self.derive.with_merge(source, target, modifiers)
|
|
2330
|
+
if self.derive
|
|
2331
|
+
else None
|
|
2332
|
+
),
|
|
2214
2333
|
namespace=self.namespace,
|
|
2215
2334
|
hidden_components=self.hidden_components,
|
|
2216
2335
|
order_by=(
|
|
@@ -2236,6 +2355,7 @@ class MultiSelectLineage(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
2236
2355
|
return MultiSelectLineage.model_construct(
|
|
2237
2356
|
selects=[c.with_namespace(namespace) for c in self.selects],
|
|
2238
2357
|
align=self.align.with_namespace(namespace),
|
|
2358
|
+
derive=self.derive.with_namespace(namespace) if self.derive else None,
|
|
2239
2359
|
namespace=namespace,
|
|
2240
2360
|
hidden_components=self.hidden_components,
|
|
2241
2361
|
order_by=self.order_by.with_namespace(namespace) if self.order_by else None,
|
|
@@ -2257,6 +2377,9 @@ class MultiSelectLineage(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
2257
2377
|
output = set()
|
|
2258
2378
|
for item in self.align.items:
|
|
2259
2379
|
output.add(item.aligned_concept)
|
|
2380
|
+
if self.derive:
|
|
2381
|
+
for ditem in self.derive.items:
|
|
2382
|
+
output.add(ditem.derived_concept)
|
|
2260
2383
|
return output
|
|
2261
2384
|
|
|
2262
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,
|
|
@@ -247,7 +249,7 @@ class BuildParamaterizedConceptReference(DataTyped):
|
|
|
247
249
|
concept: BuildConcept
|
|
248
250
|
|
|
249
251
|
def __str__(self):
|
|
250
|
-
return f":{self.concept.address}"
|
|
252
|
+
return f":{self.concept.address.replace('.', '_')}"
|
|
251
253
|
|
|
252
254
|
@property
|
|
253
255
|
def safe_address(self) -> str:
|
|
@@ -1268,6 +1270,22 @@ class BuildAlignClause:
|
|
|
1268
1270
|
items: List[BuildAlignItem]
|
|
1269
1271
|
|
|
1270
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
|
+
|
|
1271
1289
|
@dataclass
|
|
1272
1290
|
class BuildSelectLineage:
|
|
1273
1291
|
selection: List[BuildConcept]
|
|
@@ -1299,12 +1317,16 @@ class BuildMultiSelectLineage(BuildConceptArgs):
|
|
|
1299
1317
|
limit: Optional[int] = None
|
|
1300
1318
|
where_clause: Union["BuildWhereClause", None] = field(default=None)
|
|
1301
1319
|
having_clause: Union["BuildHavingClause", None] = field(default=None)
|
|
1320
|
+
derive: BuildDeriveClause | None = None
|
|
1302
1321
|
|
|
1303
1322
|
@property
|
|
1304
1323
|
def derived_concepts(self) -> set[str]:
|
|
1305
1324
|
output = set()
|
|
1306
1325
|
for item in self.align.items:
|
|
1307
1326
|
output.add(item.aligned_concept)
|
|
1327
|
+
if self.derive:
|
|
1328
|
+
for ditem in self.derive.items:
|
|
1329
|
+
output.add(ditem.address)
|
|
1308
1330
|
return output
|
|
1309
1331
|
|
|
1310
1332
|
@property
|
|
@@ -1312,10 +1334,12 @@ class BuildMultiSelectLineage(BuildConceptArgs):
|
|
|
1312
1334
|
return self.build_output_components
|
|
1313
1335
|
|
|
1314
1336
|
@property
|
|
1315
|
-
def
|
|
1316
|
-
output = set()
|
|
1317
|
-
|
|
1318
|
-
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)
|
|
1319
1343
|
return output
|
|
1320
1344
|
|
|
1321
1345
|
@property
|
|
@@ -1335,6 +1359,7 @@ class BuildMultiSelectLineage(BuildConceptArgs):
|
|
|
1335
1359
|
for c in x.concepts:
|
|
1336
1360
|
if c.address in cte.output_lcl:
|
|
1337
1361
|
return c
|
|
1362
|
+
|
|
1338
1363
|
raise SyntaxError(
|
|
1339
1364
|
f"Could not find upstream map for multiselect {str(concept)} on cte ({cte})"
|
|
1340
1365
|
)
|
|
@@ -1961,6 +1986,28 @@ class Factory:
|
|
|
1961
1986
|
def _build_align_clause(self, base: AlignClause) -> BuildAlignClause:
|
|
1962
1987
|
return BuildAlignClause(items=[self._build_align_item(x) for x in base.items])
|
|
1963
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
|
+
|
|
1964
2011
|
@build.register
|
|
1965
2012
|
def _(self, base: RowsetItem) -> BuildRowsetItem:
|
|
1966
2013
|
return self._build_rowset_item(base)
|
|
@@ -2162,6 +2209,7 @@ class Factory:
|
|
|
2162
2209
|
selects=base.selects,
|
|
2163
2210
|
grain=final_grain,
|
|
2164
2211
|
align=factory.build(base.align),
|
|
2212
|
+
derive=factory.build(base.derive) if base.derive else None,
|
|
2165
2213
|
# self.align.with_select_context(
|
|
2166
2214
|
# local_build_cache, self.grain, environment
|
|
2167
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.115 → pytrilogy-0.0.3.117}/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()
|
{pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/basic_node.py
RENAMED
|
@@ -7,7 +7,7 @@ from trilogy.core.models.build_environment import BuildEnvironment
|
|
|
7
7
|
from trilogy.core.processing.node_generators.common import (
|
|
8
8
|
resolve_function_parent_concepts,
|
|
9
9
|
)
|
|
10
|
-
from trilogy.core.processing.nodes import History, StrategyNode
|
|
10
|
+
from trilogy.core.processing.nodes import ConstantNode, History, StrategyNode
|
|
11
11
|
from trilogy.utility import unique
|
|
12
12
|
|
|
13
13
|
LOGGER_PREFIX = "[GEN_BASIC_NODE]"
|
|
@@ -51,11 +51,14 @@ def gen_basic_node(
|
|
|
51
51
|
)
|
|
52
52
|
synonyms: list[BuildConcept] = []
|
|
53
53
|
ignored_optional: set[str] = set()
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
# when we are getting an attribute, if there is anything else
|
|
56
56
|
# that is an attribute of the same struct in local optional
|
|
57
57
|
# select that value for discovery as well
|
|
58
|
-
if
|
|
58
|
+
if (
|
|
59
|
+
isinstance(concept.lineage, BuildFunction)
|
|
60
|
+
and concept.lineage.operator == FunctionType.ATTR_ACCESS
|
|
61
|
+
):
|
|
59
62
|
logger.info(
|
|
60
63
|
f"{depth_prefix}{LOGGER_PREFIX} checking for synonyms for attribute access"
|
|
61
64
|
)
|
|
@@ -106,20 +109,28 @@ def gen_basic_node(
|
|
|
106
109
|
logger.info(
|
|
107
110
|
f"{depth_prefix}{LOGGER_PREFIX} Fetching parents {[x.address for x in all_parents]}"
|
|
108
111
|
)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
if all_parents:
|
|
113
|
+
parent_node: StrategyNode | None = source_concepts(
|
|
114
|
+
mandatory_list=all_parents,
|
|
115
|
+
environment=environment,
|
|
116
|
+
g=g,
|
|
117
|
+
depth=depth + 1,
|
|
118
|
+
history=history,
|
|
119
|
+
conditions=conditions,
|
|
120
|
+
)
|
|
117
121
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
if not parent_node:
|
|
123
|
+
logger.info(
|
|
124
|
+
f"{depth_prefix}{LOGGER_PREFIX} No basic node could be generated for {concept}"
|
|
125
|
+
)
|
|
126
|
+
return None
|
|
127
|
+
else:
|
|
128
|
+
return ConstantNode(
|
|
129
|
+
input_concepts=[],
|
|
130
|
+
output_concepts=[concept],
|
|
131
|
+
environment=environment,
|
|
132
|
+
depth=depth,
|
|
121
133
|
)
|
|
122
|
-
return None
|
|
123
134
|
if parent_node.source_type != SourceType.CONSTANT:
|
|
124
135
|
parent_node.source_type = SourceType.BASIC
|
|
125
136
|
parent_node.add_output_concept(concept)
|
{pytrilogy-0.0.3.115 → pytrilogy-0.0.3.117}/trilogy/core/processing/node_generators/common.py
RENAMED
|
@@ -4,6 +4,7 @@ from typing import Callable, List, Tuple
|
|
|
4
4
|
from trilogy.core.enums import Derivation, Purpose
|
|
5
5
|
from trilogy.core.models.build import (
|
|
6
6
|
BuildAggregateWrapper,
|
|
7
|
+
BuildComparison,
|
|
7
8
|
BuildConcept,
|
|
8
9
|
BuildFilterItem,
|
|
9
10
|
BuildFunction,
|
|
@@ -26,7 +27,9 @@ FUNCTION_TYPES = (BuildFunction,)
|
|
|
26
27
|
def resolve_function_parent_concepts(
|
|
27
28
|
concept: BuildConcept, environment: BuildEnvironment
|
|
28
29
|
) -> List[BuildConcept]:
|
|
29
|
-
if not isinstance(
|
|
30
|
+
if not isinstance(
|
|
31
|
+
concept.lineage, (*FUNCTION_TYPES, *AGGREGATE_TYPES, BuildComparison)
|
|
32
|
+
):
|
|
30
33
|
raise ValueError(
|
|
31
34
|
f"Concept {concept} lineage is not function or aggregate, is {type(concept.lineage)}"
|
|
32
35
|
)
|
|
@@ -157,19 +157,19 @@ def gen_multiselect_node(
|
|
|
157
157
|
possible_joins = concept_to_relevant_joins(additional_relevant)
|
|
158
158
|
if not local_optional:
|
|
159
159
|
logger.info(
|
|
160
|
-
f"{padding(depth)}{LOGGER_PREFIX} no enrichment required for
|
|
160
|
+
f"{padding(depth)}{LOGGER_PREFIX} no enrichment required for multiselect node; exiting early"
|
|
161
161
|
)
|
|
162
162
|
return node
|
|
163
163
|
if not possible_joins:
|
|
164
164
|
logger.info(
|
|
165
|
-
f"{padding(depth)}{LOGGER_PREFIX} no possible joins for
|
|
165
|
+
f"{padding(depth)}{LOGGER_PREFIX} no possible joins for multiselect node; exiting early"
|
|
166
166
|
)
|
|
167
167
|
return node
|
|
168
168
|
if all(
|
|
169
169
|
[x.address in [y.address for y in node.output_concepts] for x in local_optional]
|
|
170
170
|
):
|
|
171
171
|
logger.info(
|
|
172
|
-
f"{padding(depth)}{LOGGER_PREFIX} all enriched concepts returned from base
|
|
172
|
+
f"{padding(depth)}{LOGGER_PREFIX} all enriched concepts returned from base multiselect node; exiting early"
|
|
173
173
|
)
|
|
174
174
|
return node
|
|
175
175
|
logger.info(
|
|
@@ -21,6 +21,7 @@ from trilogy.core.models.author import (
|
|
|
21
21
|
Concept,
|
|
22
22
|
ConceptRef,
|
|
23
23
|
CustomType,
|
|
24
|
+
DeriveClause,
|
|
24
25
|
Expr,
|
|
25
26
|
FilterItem,
|
|
26
27
|
Function,
|
|
@@ -352,11 +353,13 @@ class MultiSelectStatement(HasUUID, SelectTypeMixin, BaseModel):
|
|
|
352
353
|
local_concepts: Annotated[
|
|
353
354
|
EnvironmentConceptDict, PlainValidator(validate_concepts)
|
|
354
355
|
] = Field(default_factory=EnvironmentConceptDict)
|
|
356
|
+
derive: DeriveClause | None = None
|
|
355
357
|
|
|
356
358
|
def as_lineage(self, environment: Environment):
|
|
357
359
|
return MultiSelectLineage(
|
|
358
360
|
selects=[x.as_lineage(environment) for x in self.selects],
|
|
359
361
|
align=self.align,
|
|
362
|
+
derive=self.derive,
|
|
360
363
|
namespace=self.namespace,
|
|
361
364
|
# derived_concepts = self.derived_concepts,
|
|
362
365
|
limit=self.limit,
|
|
@@ -176,6 +176,20 @@ def struct_arg(args):
|
|
|
176
176
|
return [f"{x[1]}: {x[0]}" for x in zip(args[::2], args[1::2])]
|
|
177
177
|
|
|
178
178
|
|
|
179
|
+
def hash_from_args(val, hash_type):
|
|
180
|
+
hash_type = hash_type[1:-1]
|
|
181
|
+
if hash_type.lower() == "md5":
|
|
182
|
+
return f"md5({val})"
|
|
183
|
+
elif hash_type.lower() == "sha1":
|
|
184
|
+
return f"sha1({val})"
|
|
185
|
+
elif hash_type.lower() == "sha256":
|
|
186
|
+
return f"sha256({val})"
|
|
187
|
+
elif hash_type.lower() == "sha512":
|
|
188
|
+
return f"sha512({val})"
|
|
189
|
+
else:
|
|
190
|
+
raise ValueError(f"Unsupported hash type: {hash_type}")
|
|
191
|
+
|
|
192
|
+
|
|
179
193
|
FUNCTION_MAP = {
|
|
180
194
|
# generic types
|
|
181
195
|
FunctionType.ALIAS: lambda x: f"{x[0]}",
|
|
@@ -260,6 +274,7 @@ FUNCTION_MAP = {
|
|
|
260
274
|
FunctionType.REGEXP_REPLACE: lambda x: f"REGEXP_REPLACE({x[0]},{x[1]}, {x[2]})",
|
|
261
275
|
FunctionType.TRIM: lambda x: f"TRIM({x[0]})",
|
|
262
276
|
FunctionType.REPLACE: lambda x: f"REPLACE({x[0]},{x[1]},{x[2]})",
|
|
277
|
+
FunctionType.HASH: lambda x: hash_from_args(x[0], x[1]),
|
|
263
278
|
# FunctionType.NOT_LIKE: lambda x: f" CASE WHEN {x[0]} like {x[1]} THEN 0 ELSE 1 END",
|
|
264
279
|
# date types
|
|
265
280
|
FunctionType.DATE_TRUNCATE: lambda x: f"date_trunc({x[0]},{x[1]})",
|
|
@@ -484,7 +499,20 @@ class BaseDialect:
|
|
|
484
499
|
elif isinstance(c.lineage, BuildRowsetItem):
|
|
485
500
|
rval = f"{self.render_concept_sql(c.lineage.content, cte=cte, alias=False, raise_invalid=raise_invalid)}"
|
|
486
501
|
elif isinstance(c.lineage, BuildMultiSelectLineage):
|
|
487
|
-
|
|
502
|
+
if c.address in c.lineage.calculated_derivations:
|
|
503
|
+
assert c.lineage.derive is not None
|
|
504
|
+
for x in c.lineage.derive.items:
|
|
505
|
+
if x.address == c.address:
|
|
506
|
+
rval = self.render_expr(
|
|
507
|
+
x.expr,
|
|
508
|
+
cte=cte,
|
|
509
|
+
raise_invalid=raise_invalid,
|
|
510
|
+
)
|
|
511
|
+
break
|
|
512
|
+
else:
|
|
513
|
+
rval = f"{self.render_concept_sql(c.lineage.find_source(c, cte), cte=cte, alias=False, raise_invalid=raise_invalid)}"
|
|
514
|
+
elif isinstance(c.lineage, BuildComparison):
|
|
515
|
+
rval = f"{self.render_expr(c.lineage.left, cte=cte, raise_invalid=raise_invalid)} {c.lineage.operator.value} {self.render_expr(c.lineage.right, cte=cte, raise_invalid=raise_invalid)}"
|
|
488
516
|
elif isinstance(c.lineage, AGGREGATE_ITEMS):
|
|
489
517
|
args = [
|
|
490
518
|
self.render_expr(v, cte) # , alias=False)
|
|
@@ -816,7 +844,7 @@ class BaseDialect:
|
|
|
816
844
|
if self.rendering.parameters:
|
|
817
845
|
if e.concept.namespace == DEFAULT_NAMESPACE:
|
|
818
846
|
return f":{e.concept.name}"
|
|
819
|
-
return f":{e.concept.address}"
|
|
847
|
+
return f":{e.concept.address.replace('.', '_')}"
|
|
820
848
|
elif e.concept.lineage:
|
|
821
849
|
return self.render_expr(e.concept.lineage, cte=cte, cte_map=cte_map)
|
|
822
850
|
return f"{self.QUOTE_CHARACTER}{e.concept.address}{self.QUOTE_CHARACTER}"
|