pytrilogy 0.0.3.18__tar.gz → 0.0.3.20__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.
- {pytrilogy-0.0.3.18/pytrilogy.egg-info → pytrilogy-0.0.3.20}/PKG-INFO +2 -2
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/README.md +2 -2
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20/pytrilogy.egg-info}/PKG-INFO +2 -2
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_parsing.py +43 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/enums.py +1 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/models/author.py +72 -1
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/models/environment.py +10 -2
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/models/execute.py +8 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/base.py +3 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/common.py +8 -1
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/snowflake.py +3 -1
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/parsing/parse_engine.py +12 -19
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/pytrilogy.egg-info/SOURCES.txt +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/setup.cfg +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/setup.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/authoring/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/functions.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/models/build.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/optimizations/inline_constant.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/statements/author.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/parsing/common.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/parsing/trilogy.lark +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/utility.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.3.
|
|
3
|
+
Version: 0.0.3.20
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -44,7 +44,7 @@ pytrilogy is an experimental implementation of the Trilogy language, a higher-le
|
|
|
44
44
|
Trilogy looks like SQL, but simpler. It's a modern SQL refresh targeted at SQL lovers who want more reusability and composability without losing the expressiveness and iterative value of SQL. It compiles to SQL - making it easy to debug or integrate into existing workflows - and can be run against any supported SQL backend.
|
|
45
45
|
|
|
46
46
|
> [!TIP]
|
|
47
|
-
> To get an overview of the language and run interactive examples, head to the [documentation](https://trilogydata.dev/).
|
|
47
|
+
> Try it online in a hosted [open-source studio](https://trilogydata.dev/trilogy-studio-core/). To get an overview of the language and run interactive examples, head to the [documentation](https://trilogydata.dev/).
|
|
48
48
|
|
|
49
49
|
Installation: `pip install pytrilogy`
|
|
50
50
|
|
|
@@ -7,7 +7,7 @@ pytrilogy is an experimental implementation of the Trilogy language, a higher-le
|
|
|
7
7
|
Trilogy looks like SQL, but simpler. It's a modern SQL refresh targeted at SQL lovers who want more reusability and composability without losing the expressiveness and iterative value of SQL. It compiles to SQL - making it easy to debug or integrate into existing workflows - and can be run against any supported SQL backend.
|
|
8
8
|
|
|
9
9
|
> [!TIP]
|
|
10
|
-
> To get an overview of the language and run interactive examples, head to the [documentation](https://trilogydata.dev/).
|
|
10
|
+
> Try it online in a hosted [open-source studio](https://trilogydata.dev/trilogy-studio-core/). To get an overview of the language and run interactive examples, head to the [documentation](https://trilogydata.dev/).
|
|
11
11
|
|
|
12
12
|
Installation: `pip install pytrilogy`
|
|
13
13
|
|
|
@@ -353,4 +353,4 @@ Return generated SQL without executing.
|
|
|
353
353
|
|
|
354
354
|
```sql
|
|
355
355
|
show <select>;
|
|
356
|
-
```
|
|
356
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.3.
|
|
3
|
+
Version: 0.0.3.20
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -44,7 +44,7 @@ pytrilogy is an experimental implementation of the Trilogy language, a higher-le
|
|
|
44
44
|
Trilogy looks like SQL, but simpler. It's a modern SQL refresh targeted at SQL lovers who want more reusability and composability without losing the expressiveness and iterative value of SQL. It compiles to SQL - making it easy to debug or integrate into existing workflows - and can be run against any supported SQL backend.
|
|
45
45
|
|
|
46
46
|
> [!TIP]
|
|
47
|
-
> To get an overview of the language and run interactive examples, head to the [documentation](https://trilogydata.dev/).
|
|
47
|
+
> Try it online in a hosted [open-source studio](https://trilogydata.dev/trilogy-studio-core/). To get an overview of the language and run interactive examples, head to the [documentation](https://trilogydata.dev/).
|
|
48
48
|
|
|
49
49
|
Installation: `pip install pytrilogy`
|
|
50
50
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from pytest import raises
|
|
2
|
+
|
|
1
3
|
from trilogy import Dialects
|
|
2
4
|
from trilogy.constants import MagicConstants
|
|
3
5
|
from trilogy.core.enums import BooleanOperator, ComparisonOperator, Purpose
|
|
@@ -661,3 +663,44 @@ select x % 10 -> x_mod_10;
|
|
|
661
663
|
|
|
662
664
|
"""
|
|
663
665
|
)
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
def test_import_shows_source():
|
|
669
|
+
|
|
670
|
+
env = Environment(
|
|
671
|
+
config=EnvironmentOptions(
|
|
672
|
+
import_resolver=DictImportResolver(
|
|
673
|
+
content={
|
|
674
|
+
"test": """
|
|
675
|
+
import test_dep as test_dep;
|
|
676
|
+
key x int;
|
|
677
|
+
datasource test (
|
|
678
|
+
x: x)
|
|
679
|
+
grain(x)
|
|
680
|
+
query '''
|
|
681
|
+
select 1 as x
|
|
682
|
+
union all
|
|
683
|
+
select 11 as x
|
|
684
|
+
''' TYPO
|
|
685
|
+
""",
|
|
686
|
+
"test_dep": """
|
|
687
|
+
key x int;
|
|
688
|
+
""",
|
|
689
|
+
}
|
|
690
|
+
)
|
|
691
|
+
)
|
|
692
|
+
)
|
|
693
|
+
assert isinstance(env.config.import_resolver, DictImportResolver)
|
|
694
|
+
|
|
695
|
+
with raises(Exception, match="Unable to import 'test', parsing error") as e:
|
|
696
|
+
env.parse(
|
|
697
|
+
"""
|
|
698
|
+
import test;
|
|
699
|
+
|
|
700
|
+
select x % 10 -> x_mod_10;
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
"""
|
|
704
|
+
)
|
|
705
|
+
assert "TYPO" in str(e.value)
|
|
706
|
+
assert 1 == 0
|
|
@@ -318,6 +318,21 @@ class Conditional(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
318
318
|
operator=self.operator,
|
|
319
319
|
)
|
|
320
320
|
|
|
321
|
+
def with_reference_replacement(self, source, target):
|
|
322
|
+
return self.__class__.model_construct(
|
|
323
|
+
left=(
|
|
324
|
+
self.left.with_reference_replacement(source, target)
|
|
325
|
+
if isinstance(self.left, Mergeable)
|
|
326
|
+
else self.left
|
|
327
|
+
),
|
|
328
|
+
right=(
|
|
329
|
+
self.right.with_reference_replacement(source, target)
|
|
330
|
+
if isinstance(self.right, Mergeable)
|
|
331
|
+
else self.right
|
|
332
|
+
),
|
|
333
|
+
operator=self.operator,
|
|
334
|
+
)
|
|
335
|
+
|
|
321
336
|
@property
|
|
322
337
|
def concept_arguments(self) -> Sequence[ConceptRef]:
|
|
323
338
|
"""Return concepts directly referenced in where clause"""
|
|
@@ -2135,6 +2150,52 @@ class AlignItem(Namespaced, BaseModel):
|
|
|
2135
2150
|
)
|
|
2136
2151
|
|
|
2137
2152
|
|
|
2153
|
+
class CustomFunctionFactory:
|
|
2154
|
+
def __init__(
|
|
2155
|
+
self, function: Expr, namespace: str, function_arguments: list[ArgBinding]
|
|
2156
|
+
):
|
|
2157
|
+
self.namespace = namespace
|
|
2158
|
+
self.function = function
|
|
2159
|
+
self.function_arguments = function_arguments
|
|
2160
|
+
|
|
2161
|
+
def with_namespace(self, namespace: str):
|
|
2162
|
+
self.namespace = namespace
|
|
2163
|
+
self.function = (
|
|
2164
|
+
self.function.with_namespace(namespace)
|
|
2165
|
+
if isinstance(self.function, Namespaced)
|
|
2166
|
+
else self.function
|
|
2167
|
+
)
|
|
2168
|
+
self.function_arguments = [
|
|
2169
|
+
x.with_namespace(namespace) for x in self.function_arguments
|
|
2170
|
+
]
|
|
2171
|
+
return self
|
|
2172
|
+
|
|
2173
|
+
def __call__(self, *creation_args: list[Expr]):
|
|
2174
|
+
nout = (
|
|
2175
|
+
self.function.model_copy(deep=True)
|
|
2176
|
+
if isinstance(self.function, BaseModel)
|
|
2177
|
+
else self.function
|
|
2178
|
+
)
|
|
2179
|
+
creation_arg_list: list[Expr] = list(creation_args)
|
|
2180
|
+
if len(creation_args) < len(self.function_arguments):
|
|
2181
|
+
for binding in self.function_arguments[len(creation_arg_list) :]:
|
|
2182
|
+
if binding.default is None:
|
|
2183
|
+
raise ValueError(f"Missing argument {binding.name}")
|
|
2184
|
+
creation_arg_list.append(binding.default)
|
|
2185
|
+
if isinstance(nout, Mergeable):
|
|
2186
|
+
for idx, x in enumerate(creation_arg_list):
|
|
2187
|
+
if self.namespace == DEFAULT_NAMESPACE:
|
|
2188
|
+
target = f"{DEFAULT_NAMESPACE}.{self.function_arguments[idx].name}"
|
|
2189
|
+
else:
|
|
2190
|
+
target = self.function_arguments[idx].name
|
|
2191
|
+
nout = (
|
|
2192
|
+
nout.with_reference_replacement(target, x)
|
|
2193
|
+
if isinstance(nout, Mergeable)
|
|
2194
|
+
else nout
|
|
2195
|
+
)
|
|
2196
|
+
return nout
|
|
2197
|
+
|
|
2198
|
+
|
|
2138
2199
|
class Metadata(BaseModel):
|
|
2139
2200
|
"""Metadata container object.
|
|
2140
2201
|
TODO: support arbitrary tags"""
|
|
@@ -2164,10 +2225,20 @@ class Comment(BaseModel):
|
|
|
2164
2225
|
text: str
|
|
2165
2226
|
|
|
2166
2227
|
|
|
2167
|
-
class ArgBinding(BaseModel):
|
|
2228
|
+
class ArgBinding(Namespaced, BaseModel):
|
|
2168
2229
|
name: str
|
|
2169
2230
|
default: Expr | None = None
|
|
2170
2231
|
|
|
2232
|
+
def with_namespace(self, namespace):
|
|
2233
|
+
return ArgBinding(
|
|
2234
|
+
name=address_with_namespace(self.name, namespace),
|
|
2235
|
+
default=(
|
|
2236
|
+
self.default.with_namespace(namespace)
|
|
2237
|
+
if isinstance(self.default, Namespaced)
|
|
2238
|
+
else self.default
|
|
2239
|
+
),
|
|
2240
|
+
)
|
|
2241
|
+
|
|
2171
2242
|
|
|
2172
2243
|
class CustomType(BaseModel):
|
|
2173
2244
|
name: str
|
|
@@ -9,7 +9,6 @@ from typing import (
|
|
|
9
9
|
TYPE_CHECKING,
|
|
10
10
|
Annotated,
|
|
11
11
|
Any,
|
|
12
|
-
Callable,
|
|
13
12
|
Dict,
|
|
14
13
|
ItemsView,
|
|
15
14
|
List,
|
|
@@ -40,6 +39,7 @@ from trilogy.core.exceptions import (
|
|
|
40
39
|
from trilogy.core.models.author import (
|
|
41
40
|
Concept,
|
|
42
41
|
ConceptRef,
|
|
42
|
+
CustomFunctionFactory,
|
|
43
43
|
CustomType,
|
|
44
44
|
Function,
|
|
45
45
|
SelectLineage,
|
|
@@ -207,7 +207,7 @@ class Environment(BaseModel):
|
|
|
207
207
|
datasources: Annotated[
|
|
208
208
|
EnvironmentDatasourceDict, PlainValidator(validate_datasources)
|
|
209
209
|
] = Field(default_factory=EnvironmentDatasourceDict)
|
|
210
|
-
functions: Dict[str,
|
|
210
|
+
functions: Dict[str, CustomFunctionFactory] = Field(default_factory=dict)
|
|
211
211
|
data_types: Dict[str, CustomType] = Field(default_factory=dict)
|
|
212
212
|
named_statements: Dict[str, SelectLineage] = Field(default_factory=dict)
|
|
213
213
|
imports: Dict[str, list[Import]] = Field(
|
|
@@ -434,6 +434,14 @@ class Environment(BaseModel):
|
|
|
434
434
|
self.alias_origin_lookup[address_with_namespace(key, alias)] = (
|
|
435
435
|
val.with_namespace(alias)
|
|
436
436
|
)
|
|
437
|
+
|
|
438
|
+
for key, function in source.functions.items():
|
|
439
|
+
if same_namespace:
|
|
440
|
+
self.functions[key] = function
|
|
441
|
+
else:
|
|
442
|
+
self.functions[address_with_namespace(key, alias)] = (
|
|
443
|
+
function.with_namespace(alias)
|
|
444
|
+
)
|
|
437
445
|
return self
|
|
438
446
|
|
|
439
447
|
def add_file_import(
|
|
@@ -59,6 +59,14 @@ class CTE(BaseModel):
|
|
|
59
59
|
base_name_override: Optional[str] = None
|
|
60
60
|
base_alias_override: Optional[str] = None
|
|
61
61
|
|
|
62
|
+
@field_validator("join_derived_concepts")
|
|
63
|
+
def validate_join_derived_concepts(cls, v):
|
|
64
|
+
if len(v) > 1:
|
|
65
|
+
raise NotImplementedError(
|
|
66
|
+
"Multiple join derived concepts not yet supported."
|
|
67
|
+
)
|
|
68
|
+
return unique(v, "address")
|
|
69
|
+
|
|
62
70
|
@property
|
|
63
71
|
def identifier(self):
|
|
64
72
|
return self.name
|
|
@@ -694,6 +694,7 @@ class BaseDialect:
|
|
|
694
694
|
UnnestMode.CROSS_APPLY,
|
|
695
695
|
UnnestMode.CROSS_JOIN,
|
|
696
696
|
UnnestMode.CROSS_JOIN_ALIAS,
|
|
697
|
+
UnnestMode.SNOWFLAKE,
|
|
697
698
|
):
|
|
698
699
|
# for a cross apply, derivation happens in the join
|
|
699
700
|
# so we only use the alias to select
|
|
@@ -722,7 +723,9 @@ class BaseDialect:
|
|
|
722
723
|
UnnestMode.CROSS_JOIN_ALIAS,
|
|
723
724
|
UnnestMode.CROSS_JOIN,
|
|
724
725
|
UnnestMode.CROSS_APPLY,
|
|
726
|
+
UnnestMode.SNOWFLAKE,
|
|
725
727
|
):
|
|
728
|
+
|
|
726
729
|
source = f"{render_unnest(self.UNNEST_MODE, self.QUOTE_CHARACTER, cte.join_derived_concepts[0], self.render_concept_sql, cte)}"
|
|
727
730
|
# direct - eg DUCK DB - can be directly selected inline
|
|
728
731
|
elif (
|
|
@@ -25,7 +25,12 @@ def render_unnest(
|
|
|
25
25
|
):
|
|
26
26
|
if unnest_mode == UnnestMode.CROSS_JOIN:
|
|
27
27
|
return f"{render_func(concept, cte, False)} as {quote_character}{concept.safe_address}{quote_character}"
|
|
28
|
-
|
|
28
|
+
elif unnest_mode == UnnestMode.CROSS_JOIN_ALIAS:
|
|
29
|
+
return f"{render_func(concept, cte, False)} as unnest_wrapper ({quote_character}{concept.safe_address}{quote_character})"
|
|
30
|
+
elif unnest_mode == UnnestMode.SNOWFLAKE:
|
|
31
|
+
|
|
32
|
+
return f"flatten({render_func(concept, cte, False)}) as unnest_wrapper ( unnest1, unnest2, unnest3, unnest4, {quote_character}{cte.join_derived_concepts[0].safe_address}{quote_character})"
|
|
33
|
+
return f"{render_func(concept, cte, False)} as {quote_character}{concept.safe_address}{quote_character}"
|
|
29
34
|
|
|
30
35
|
|
|
31
36
|
def render_join_concept(
|
|
@@ -67,6 +72,8 @@ def render_join(
|
|
|
67
72
|
return f"CROSS JOIN {render_unnest(unnest_mode, quote_character, join.concept_to_unnest, render_func, cte)}"
|
|
68
73
|
if unnest_mode == UnnestMode.CROSS_JOIN_ALIAS:
|
|
69
74
|
return f"CROSS JOIN {render_unnest(unnest_mode, quote_character, join.concept_to_unnest, render_func, cte)}"
|
|
75
|
+
if unnest_mode == UnnestMode.SNOWFLAKE:
|
|
76
|
+
return f"LEFT JOIN LATERAL {render_unnest(unnest_mode, quote_character, join.concept_to_unnest, render_func, cte)}"
|
|
70
77
|
return f"FULL JOIN {render_unnest(unnest_mode, quote_character, join.concept_to_unnest, render_func, cte)}"
|
|
71
78
|
# left_name = join.left_name
|
|
72
79
|
right_name = join.right_name
|
|
@@ -30,6 +30,8 @@ FUNCTION_MAP = {
|
|
|
30
30
|
FunctionType.QUARTER: lambda x: f"EXTRACT(QUARTER from {x[0]})",
|
|
31
31
|
# math
|
|
32
32
|
FunctionType.DIVIDE: lambda x: f"DIV0({x[0]},{x[1]})",
|
|
33
|
+
FunctionType.UNNEST: lambda x: f"table(flatten({x[0]}))",
|
|
34
|
+
FunctionType.ARRAY: lambda x: f"ARRAY_CONSTRUCT({', '.join(x)})",
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
FUNCTION_GRAIN_MATCH_MAP = {
|
|
@@ -83,4 +85,4 @@ class SnowflakeDialect(BaseDialect):
|
|
|
83
85
|
}
|
|
84
86
|
QUOTE_CHARACTER = '"'
|
|
85
87
|
SQL_TEMPLATE = BQ_SQL_TEMPLATE
|
|
86
|
-
UNNEST_MODE = UnnestMode.
|
|
88
|
+
UNNEST_MODE = UnnestMode.SNOWFLAKE
|
|
@@ -57,13 +57,13 @@ from trilogy.core.models.author import (
|
|
|
57
57
|
Concept,
|
|
58
58
|
ConceptRef,
|
|
59
59
|
Conditional,
|
|
60
|
+
CustomFunctionFactory,
|
|
60
61
|
CustomType,
|
|
61
62
|
Expr,
|
|
62
63
|
FilterItem,
|
|
63
64
|
Function,
|
|
64
65
|
Grain,
|
|
65
66
|
HavingClause,
|
|
66
|
-
Mergeable,
|
|
67
67
|
Metadata,
|
|
68
68
|
OrderBy,
|
|
69
69
|
OrderItem,
|
|
@@ -983,7 +983,12 @@ class ParseToObjects(Transformer):
|
|
|
983
983
|
text = self.resolve_import_address(target)
|
|
984
984
|
self.text_lookup[token_lookup] = text
|
|
985
985
|
|
|
986
|
-
|
|
986
|
+
try:
|
|
987
|
+
raw_tokens = PARSER.parse(text)
|
|
988
|
+
except Exception as e:
|
|
989
|
+
raise ImportError(
|
|
990
|
+
f"Unable to import '{target}', parsing error: {e}"
|
|
991
|
+
) from e
|
|
987
992
|
self.tokens[token_lookup] = raw_tokens
|
|
988
993
|
|
|
989
994
|
if cache_lookup in self.parsed:
|
|
@@ -1198,23 +1203,11 @@ class ParseToObjects(Transformer):
|
|
|
1198
1203
|
function_arguments: list[ArgBinding] = args[1]
|
|
1199
1204
|
output = args[2]
|
|
1200
1205
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
if binding.default is None:
|
|
1207
|
-
raise ValueError(f"Missing argument {binding.name}")
|
|
1208
|
-
creation_arg_list.append(binding.default)
|
|
1209
|
-
if isinstance(nout, Mergeable):
|
|
1210
|
-
for idx, x in enumerate(creation_arg_list):
|
|
1211
|
-
# these will always be local namespace
|
|
1212
|
-
nout = nout.with_reference_replacement(
|
|
1213
|
-
f"{DEFAULT_NAMESPACE}.{function_arguments[idx].name}", x
|
|
1214
|
-
)
|
|
1215
|
-
return nout
|
|
1216
|
-
|
|
1217
|
-
self.environment.functions[identity] = function_factory
|
|
1206
|
+
self.environment.functions[identity] = CustomFunctionFactory(
|
|
1207
|
+
function=output,
|
|
1208
|
+
namespace=self.environment.namespace,
|
|
1209
|
+
function_arguments=function_arguments,
|
|
1210
|
+
)
|
|
1218
1211
|
return FunctionDeclaration(name=identity, args=function_arguments, expr=output)
|
|
1219
1212
|
|
|
1220
1213
|
def custom_function(self, args):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/__init__.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/basic_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/filter_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/group_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/group_to_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/node_merge_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/rowset_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/select_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/synonym_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/union_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/unnest_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.18 → pytrilogy-0.0.3.20}/trilogy/core/processing/node_generators/window_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|