pytrilogy 0.0.3.9__tar.gz → 0.0.3.11__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.9/pytrilogy.egg-info → pytrilogy-0.0.3.11}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/pytrilogy.egg-info/SOURCES.txt +3 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_parsing.py +43 -1
- pytrilogy-0.0.3.11/tests/test_query_render.py +13 -0
- pytrilogy-0.0.3.11/tests/test_typing.py +42 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/authoring/__init__.py +14 -1
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/enums.py +1 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/functions.py +9 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/models/author.py +13 -7
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/models/core.py +13 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/models/environment.py +20 -4
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/nodes/group_node.py +1 -1
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/statements/author.py +13 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/base.py +1 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/bigquery.py +2 -3
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/duckdb.py +2 -2
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/enums.py +5 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/presto.py +1 -1
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/sql_server.py +1 -1
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/executor.py +2 -34
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/parsing/parse_engine.py +59 -13
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/parsing/trilogy.lark +12 -8
- pytrilogy-0.0.3.11/trilogy/render.py +38 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/README.md +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/setup.cfg +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/setup.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/models/build.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/models/execute.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/optimizations/inline_constant.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/parsing/common.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.9 → pytrilogy-0.0.3.11}/trilogy/utility.py +0 -0
|
@@ -24,9 +24,11 @@ tests/test_parse_engine.py
|
|
|
24
24
|
tests/test_parsing.py
|
|
25
25
|
tests/test_partial_handling.py
|
|
26
26
|
tests/test_query_processing.py
|
|
27
|
+
tests/test_query_render.py
|
|
27
28
|
tests/test_select.py
|
|
28
29
|
tests/test_show.py
|
|
29
30
|
tests/test_statements.py
|
|
31
|
+
tests/test_typing.py
|
|
30
32
|
tests/test_undefined_concept.py
|
|
31
33
|
tests/test_user_functions.py
|
|
32
34
|
tests/test_where_clause.py
|
|
@@ -37,6 +39,7 @@ trilogy/engine.py
|
|
|
37
39
|
trilogy/executor.py
|
|
38
40
|
trilogy/parser.py
|
|
39
41
|
trilogy/py.typed
|
|
42
|
+
trilogy/render.py
|
|
40
43
|
trilogy/utility.py
|
|
41
44
|
trilogy/authoring/__init__.py
|
|
42
45
|
trilogy/core/__init__.py
|
|
@@ -8,7 +8,11 @@ from trilogy.core.models.core import (
|
|
|
8
8
|
TupleWrapper,
|
|
9
9
|
)
|
|
10
10
|
from trilogy.core.models.datasource import Datasource
|
|
11
|
-
from trilogy.core.models.environment import
|
|
11
|
+
from trilogy.core.models.environment import (
|
|
12
|
+
DictImportResolver,
|
|
13
|
+
Environment,
|
|
14
|
+
EnvironmentOptions,
|
|
15
|
+
)
|
|
12
16
|
from trilogy.core.statements.author import SelectStatement, ShowStatement
|
|
13
17
|
from trilogy.core.statements.execute import ProcessedQuery
|
|
14
18
|
from trilogy.dialect.base import BaseDialect
|
|
@@ -619,3 +623,41 @@ select [1,2,3,4] as int_array, 2 as scalar
|
|
|
619
623
|
|
|
620
624
|
env, parsed = parse_text(x)
|
|
621
625
|
assert env.concepts["split"].datatype == DataType.INTEGER
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def test_non_file_imports():
|
|
629
|
+
|
|
630
|
+
env = Environment(
|
|
631
|
+
config=EnvironmentOptions(
|
|
632
|
+
import_resolver=DictImportResolver(
|
|
633
|
+
content={
|
|
634
|
+
"test": """
|
|
635
|
+
import test_dep as test_dep;
|
|
636
|
+
key x int;
|
|
637
|
+
|
|
638
|
+
datasource test (
|
|
639
|
+
x: x)
|
|
640
|
+
grain(x)
|
|
641
|
+
query '''
|
|
642
|
+
select 1 as x
|
|
643
|
+
union all
|
|
644
|
+
select 11 as x
|
|
645
|
+
''';
|
|
646
|
+
""",
|
|
647
|
+
"test_dep": """
|
|
648
|
+
key x int;
|
|
649
|
+
""",
|
|
650
|
+
}
|
|
651
|
+
)
|
|
652
|
+
)
|
|
653
|
+
)
|
|
654
|
+
assert isinstance(env.config.import_resolver, DictImportResolver)
|
|
655
|
+
env.parse(
|
|
656
|
+
"""
|
|
657
|
+
import test;
|
|
658
|
+
|
|
659
|
+
select x % 10 -> x_mod_10;
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
"""
|
|
663
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from trilogy import Dialects, Environment
|
|
2
|
+
from trilogy.core.query_processor import process_query
|
|
3
|
+
from trilogy.render import get_dialect_generator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_sql_generators():
|
|
7
|
+
env = Environment()
|
|
8
|
+
_, statements = env.parse("""const a <- 1; select a;""")
|
|
9
|
+
processed = process_query(env, statements[-1])
|
|
10
|
+
for dialect in Dialects:
|
|
11
|
+
generator = get_dialect_generator(dialect)
|
|
12
|
+
compiled = generator.compile_statement(processed)
|
|
13
|
+
assert compiled.startswith("""SELECT\n :a"""), f'{dialect} compiled'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from trilogy import Dialects
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_typing():
|
|
5
|
+
env = Dialects.DUCK_DB.default_executor()
|
|
6
|
+
env.environment.parse(
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
type email string;
|
|
10
|
+
|
|
11
|
+
key customer_id int;
|
|
12
|
+
property customer_id.email string::email;
|
|
13
|
+
|
|
14
|
+
def is_valid_email(email) -> contains(email, '@');
|
|
15
|
+
|
|
16
|
+
datasource customers (
|
|
17
|
+
id:customer_id,
|
|
18
|
+
email: email
|
|
19
|
+
)
|
|
20
|
+
grain (customer_id)
|
|
21
|
+
query '''
|
|
22
|
+
select 1 as id, 'bright@gmail.com' as email
|
|
23
|
+
union all
|
|
24
|
+
select 2 as id, 'funky@hotmail.com' as email
|
|
25
|
+
''';
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
results = env.execute_query(
|
|
32
|
+
"""SELECT
|
|
33
|
+
customer_id,
|
|
34
|
+
@is_valid_email(email)->valid;"""
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
for row in results.fetchall():
|
|
38
|
+
assert row.valid is True
|
|
39
|
+
|
|
40
|
+
assert "email" in env.environment.data_types
|
|
41
|
+
|
|
42
|
+
assert env.environment.concepts["email"].datatype.traits == ["email"]
|
|
@@ -33,11 +33,20 @@ from trilogy.core.models.author import (
|
|
|
33
33
|
WindowOrder,
|
|
34
34
|
WindowType,
|
|
35
35
|
)
|
|
36
|
-
from trilogy.core.models.core import
|
|
36
|
+
from trilogy.core.models.core import (
|
|
37
|
+
DataType,
|
|
38
|
+
ListType,
|
|
39
|
+
ListWrapper,
|
|
40
|
+
MapType,
|
|
41
|
+
StructType,
|
|
42
|
+
)
|
|
37
43
|
from trilogy.core.models.environment import Environment
|
|
38
44
|
from trilogy.core.statements.author import (
|
|
39
45
|
ConceptDeclarationStatement,
|
|
40
46
|
ConceptTransform,
|
|
47
|
+
MultiSelectStatement,
|
|
48
|
+
PersistStatement,
|
|
49
|
+
RawSQLStatement,
|
|
41
50
|
SelectItem,
|
|
42
51
|
SelectStatement,
|
|
43
52
|
)
|
|
@@ -60,6 +69,7 @@ __all__ = [
|
|
|
60
69
|
"DataType",
|
|
61
70
|
"StructType",
|
|
62
71
|
"ListType",
|
|
72
|
+
"MapType",
|
|
63
73
|
"ListWrapper",
|
|
64
74
|
"FunctionType",
|
|
65
75
|
"FunctionFactory",
|
|
@@ -87,4 +97,7 @@ __all__ = [
|
|
|
87
97
|
"DEFAULT_NAMESPACE",
|
|
88
98
|
"arbitrary_to_concept",
|
|
89
99
|
"arg_to_datatype",
|
|
100
|
+
"MultiSelectStatement",
|
|
101
|
+
"PersistStatement",
|
|
102
|
+
"RawSQLStatement",
|
|
90
103
|
]
|
|
@@ -271,6 +271,15 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
271
271
|
output_type=DataType.INTEGER,
|
|
272
272
|
arg_count=2,
|
|
273
273
|
),
|
|
274
|
+
FunctionType.CONTAINS: FunctionConfig(
|
|
275
|
+
valid_inputs=[
|
|
276
|
+
{DataType.STRING},
|
|
277
|
+
{DataType.STRING},
|
|
278
|
+
],
|
|
279
|
+
output_purpose=Purpose.PROPERTY,
|
|
280
|
+
output_type=DataType.BOOL,
|
|
281
|
+
arg_count=2,
|
|
282
|
+
),
|
|
274
283
|
FunctionType.SUBSTRING: FunctionConfig(
|
|
275
284
|
valid_inputs=[{DataType.STRING}, {DataType.INTEGER}, {DataType.INTEGER}],
|
|
276
285
|
output_purpose=Purpose.PROPERTY,
|
|
@@ -55,6 +55,7 @@ from trilogy.core.models.core import (
|
|
|
55
55
|
MapWrapper,
|
|
56
56
|
NumericType,
|
|
57
57
|
StructType,
|
|
58
|
+
TraitDataType,
|
|
58
59
|
TupleWrapper,
|
|
59
60
|
arg_to_datatype,
|
|
60
61
|
is_compatible_datatype,
|
|
@@ -101,9 +102,9 @@ class HasUUID(ABC):
|
|
|
101
102
|
|
|
102
103
|
class ConceptRef(Addressable, Namespaced, DataTyped, Mergeable, BaseModel):
|
|
103
104
|
address: str
|
|
104
|
-
datatype:
|
|
105
|
-
DataType
|
|
106
|
-
)
|
|
105
|
+
datatype: (
|
|
106
|
+
DataType | TraitDataType | ListType | StructType | MapType | NumericType
|
|
107
|
+
) = DataType.UNKNOWN
|
|
107
108
|
metadata: Optional["Metadata"] = None
|
|
108
109
|
|
|
109
110
|
@property
|
|
@@ -756,7 +757,7 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
756
757
|
extra="forbid",
|
|
757
758
|
)
|
|
758
759
|
name: str
|
|
759
|
-
datatype: DataType | ListType | StructType | MapType | NumericType
|
|
760
|
+
datatype: DataType | TraitDataType | ListType | StructType | MapType | NumericType
|
|
760
761
|
purpose: Purpose
|
|
761
762
|
derivation: Derivation = Derivation.ROOT
|
|
762
763
|
granularity: Granularity = Granularity.MULTI_ROW
|
|
@@ -1217,9 +1218,9 @@ class UndefinedConceptFull(Concept, Mergeable, Namespaced):
|
|
|
1217
1218
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
1218
1219
|
name: str
|
|
1219
1220
|
line_no: int | None = None
|
|
1220
|
-
datatype:
|
|
1221
|
-
DataType
|
|
1222
|
-
)
|
|
1221
|
+
datatype: (
|
|
1222
|
+
DataType | TraitDataType | ListType | StructType | MapType | NumericType
|
|
1223
|
+
) = DataType.UNKNOWN
|
|
1223
1224
|
purpose: Purpose = Purpose.UNKNOWN
|
|
1224
1225
|
|
|
1225
1226
|
@property
|
|
@@ -2153,6 +2154,11 @@ class ArgBinding(BaseModel):
|
|
|
2153
2154
|
default: Expr | None = None
|
|
2154
2155
|
|
|
2155
2156
|
|
|
2157
|
+
class CustomType(BaseModel):
|
|
2158
|
+
name: str
|
|
2159
|
+
type: DataType
|
|
2160
|
+
|
|
2161
|
+
|
|
2156
2162
|
Expr = (
|
|
2157
2163
|
MagicConstants
|
|
2158
2164
|
| bool
|
|
@@ -97,6 +97,19 @@ class DataType(Enum):
|
|
|
97
97
|
return self
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
class TraitDataType(BaseModel):
|
|
101
|
+
type: DataType
|
|
102
|
+
traits: list[str]
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def data_type(self):
|
|
106
|
+
return self.type
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def value(self):
|
|
110
|
+
return self.data_type.value
|
|
111
|
+
|
|
112
|
+
|
|
100
113
|
class NumericType(BaseModel):
|
|
101
114
|
precision: int = 20
|
|
102
115
|
scale: int = 5
|
|
@@ -40,6 +40,7 @@ from trilogy.core.exceptions import (
|
|
|
40
40
|
from trilogy.core.models.author import (
|
|
41
41
|
Concept,
|
|
42
42
|
ConceptRef,
|
|
43
|
+
CustomType,
|
|
43
44
|
Function,
|
|
44
45
|
SelectLineage,
|
|
45
46
|
UndefinedConcept,
|
|
@@ -59,8 +60,23 @@ class Import:
|
|
|
59
60
|
path: Path
|
|
60
61
|
|
|
61
62
|
|
|
63
|
+
class BaseImportResolver(BaseModel):
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class FileSystemImportResolver(BaseImportResolver):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class DictImportResolver(BaseImportResolver):
|
|
72
|
+
content: Dict[str, str]
|
|
73
|
+
|
|
74
|
+
|
|
62
75
|
class EnvironmentOptions(BaseModel):
|
|
63
76
|
allow_duplicate_declaration: bool = True
|
|
77
|
+
import_resolver: BaseImportResolver = Field(
|
|
78
|
+
default_factory=FileSystemImportResolver
|
|
79
|
+
)
|
|
64
80
|
|
|
65
81
|
|
|
66
82
|
class EnvironmentConceptDict(dict):
|
|
@@ -191,14 +207,14 @@ class Environment(BaseModel):
|
|
|
191
207
|
EnvironmentDatasourceDict, PlainValidator(validate_datasources)
|
|
192
208
|
] = Field(default_factory=EnvironmentDatasourceDict)
|
|
193
209
|
functions: Dict[str, Callable[..., Any]] = Field(default_factory=dict)
|
|
194
|
-
data_types: Dict[str,
|
|
210
|
+
data_types: Dict[str, CustomType] = Field(default_factory=dict)
|
|
195
211
|
named_statements: Dict[str, SelectLineage] = Field(default_factory=dict)
|
|
196
212
|
imports: Dict[str, list[Import]] = Field(
|
|
197
213
|
default_factory=lambda: defaultdict(list) # type: ignore
|
|
198
214
|
)
|
|
199
215
|
namespace: str = DEFAULT_NAMESPACE
|
|
200
216
|
working_path: str | Path = Field(default_factory=lambda: os.getcwd())
|
|
201
|
-
|
|
217
|
+
config: EnvironmentOptions = Field(default_factory=EnvironmentOptions)
|
|
202
218
|
version: str = Field(default_factory=get_version)
|
|
203
219
|
cte_name_map: Dict[str, str] = Field(default_factory=dict)
|
|
204
220
|
materialized_concepts: set[str] = Field(default_factory=set)
|
|
@@ -233,7 +249,7 @@ class Environment(BaseModel):
|
|
|
233
249
|
imports=dict(self.imports),
|
|
234
250
|
namespace=self.namespace,
|
|
235
251
|
working_path=self.working_path,
|
|
236
|
-
environment_config=self.
|
|
252
|
+
environment_config=self.config,
|
|
237
253
|
version=self.version,
|
|
238
254
|
cte_name_map=dict(self.cte_name_map),
|
|
239
255
|
materialized_concepts=set(self.materialized_concepts),
|
|
@@ -341,7 +357,7 @@ class Environment(BaseModel):
|
|
|
341
357
|
|
|
342
358
|
return None
|
|
343
359
|
|
|
344
|
-
if existing and self.
|
|
360
|
+
if existing and self.config.allow_duplicate_declaration:
|
|
345
361
|
if existing.metadata.concept_source == ConceptSource.PERSIST_STATEMENT:
|
|
346
362
|
return handle_persist()
|
|
347
363
|
return
|
|
@@ -92,7 +92,7 @@ class GroupNode(StrategyNode):
|
|
|
92
92
|
comp_grain += source.grain
|
|
93
93
|
for x in source.output_concepts:
|
|
94
94
|
concept_map[x.address] = x
|
|
95
|
-
lookups = [
|
|
95
|
+
lookups: list[BuildConcept | str] = [
|
|
96
96
|
concept_map[x] if x in concept_map else x for x in comp_grain.components
|
|
97
97
|
]
|
|
98
98
|
comp_grain = BuildGrain.from_concepts(lookups, environment=environment)
|
|
@@ -15,8 +15,11 @@ from trilogy.core.enums import (
|
|
|
15
15
|
from trilogy.core.models.author import (
|
|
16
16
|
AggregateWrapper,
|
|
17
17
|
AlignClause,
|
|
18
|
+
ArgBinding,
|
|
18
19
|
Concept,
|
|
19
20
|
ConceptRef,
|
|
21
|
+
CustomType,
|
|
22
|
+
Expr,
|
|
20
23
|
FilterItem,
|
|
21
24
|
Function,
|
|
22
25
|
Grain,
|
|
@@ -413,3 +416,13 @@ class ConceptDeclarationStatement(HasUUID, BaseModel):
|
|
|
413
416
|
|
|
414
417
|
class ConceptDerivationStatement(BaseModel):
|
|
415
418
|
concept: Concept
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
class TypeDeclaration(BaseModel):
|
|
422
|
+
type: CustomType
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class FunctionDeclaration(BaseModel):
|
|
426
|
+
name: str
|
|
427
|
+
args: list[ArgBinding]
|
|
428
|
+
expr: Expr
|
|
@@ -184,6 +184,7 @@ FUNCTION_MAP = {
|
|
|
184
184
|
FunctionType.LOWER: lambda x: f"LOWER({x[0]}) ",
|
|
185
185
|
FunctionType.SUBSTRING: lambda x: f"SUBSTRING({x[0]},{x[1]},{x[2]})",
|
|
186
186
|
FunctionType.STRPOS: lambda x: f"STRPOS({x[0]},{x[1]})",
|
|
187
|
+
FunctionType.CONTAINS: lambda x: f"CONTAINS({x[0]},{x[1]})",
|
|
187
188
|
# FunctionType.NOT_LIKE: lambda x: f" CASE WHEN {x[0]} like {x[1]} THEN 0 ELSE 1 END",
|
|
188
189
|
# date types
|
|
189
190
|
FunctionType.DATE_TRUNCATE: lambda x: f"date_trunc({x[0]},{x[1]})",
|
|
@@ -42,10 +42,9 @@ CREATE OR REPLACE TABLE {{ output.address.location }} AS
|
|
|
42
42
|
{% endif %}{%- if ctes %}
|
|
43
43
|
WITH {% for cte in ctes %}
|
|
44
44
|
{{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
|
|
45
|
-
{%- if full_select
|
|
45
|
+
{%- if full_select -%}
|
|
46
46
|
{{full_select}}
|
|
47
|
-
{
|
|
48
|
-
|
|
47
|
+
{%- else -%}
|
|
49
48
|
SELECT
|
|
50
49
|
{%- for select in select_columns %}
|
|
51
50
|
{{ select }}{% if not loop.last %},{% endif %}{% endfor %}
|
|
@@ -57,8 +57,8 @@ CREATE OR REPLACE TABLE {{ output.address.location }} AS
|
|
|
57
57
|
WITH {% for cte in ctes %}
|
|
58
58
|
{{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
|
|
59
59
|
{%- if full_select -%}{{full_select}}
|
|
60
|
-
{%- else -%}{%- if comment
|
|
61
|
-
-- {{ comment }}{
|
|
60
|
+
{%- else -%}{%- if comment -%}
|
|
61
|
+
-- {{ comment }}{%- endif -%}
|
|
62
62
|
SELECT
|
|
63
63
|
{%- for select in select_columns %}
|
|
64
64
|
{{ select }}{% if not loop.last %},{% endif %}{% endfor %}
|
|
@@ -41,6 +41,11 @@ class Dialects(Enum):
|
|
|
41
41
|
return cls.DUCK_DB
|
|
42
42
|
return super()._missing_(value)
|
|
43
43
|
|
|
44
|
+
def default_renderer(self, conf=None, _engine_factory: Callable = default_factory):
|
|
45
|
+
from trilogy.render import get_dialect_generator
|
|
46
|
+
|
|
47
|
+
return get_dialect_generator(self)
|
|
48
|
+
|
|
44
49
|
def default_engine(self, conf=None, _engine_factory: Callable = default_factory):
|
|
45
50
|
if self == Dialects.BIGQUERY:
|
|
46
51
|
from google.auth import default
|
|
@@ -50,7 +50,7 @@ WITH {% for cte in ctes %}
|
|
|
50
50
|
{{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
|
|
51
51
|
{%- if full_select -%}
|
|
52
52
|
{{full_select}}
|
|
53
|
-
{%- else
|
|
53
|
+
{%- else -%}
|
|
54
54
|
SELECT
|
|
55
55
|
{%- for select in select_columns %}
|
|
56
56
|
{{ select }}{% if not loop.last %},{% endif %}{% endfor %}
|
|
@@ -42,7 +42,7 @@ WITH {% for cte in ctes %}
|
|
|
42
42
|
{{cte.name}} as ({{cte.statement}}){% if not loop.last %},{% endif %}{% endfor %}{% endif %}
|
|
43
43
|
{%- if full_select -%}{{full_select}}
|
|
44
44
|
{%- else -%}{%- if comment %}
|
|
45
|
-
-- {{ comment }}{
|
|
45
|
+
-- {{ comment }}{%- endif -%}
|
|
46
46
|
SELECT
|
|
47
47
|
{%- if limit is not none %}
|
|
48
48
|
TOP {{ limit }}{% endif %}
|
|
@@ -36,6 +36,7 @@ from trilogy.dialect.enums import Dialects
|
|
|
36
36
|
from trilogy.engine import ExecutionEngine
|
|
37
37
|
from trilogy.hooks.base_hook import BaseHook
|
|
38
38
|
from trilogy.parser import parse_text
|
|
39
|
+
from trilogy.render import get_dialect_generator
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
class ResultProtocol(Protocol):
|
|
@@ -82,40 +83,7 @@ class Executor(object):
|
|
|
82
83
|
self.generator: BaseDialect
|
|
83
84
|
self.logger = logger
|
|
84
85
|
self.hooks = hooks
|
|
85
|
-
|
|
86
|
-
from trilogy.dialect.bigquery import BigqueryDialect
|
|
87
|
-
|
|
88
|
-
self.generator = BigqueryDialect()
|
|
89
|
-
elif self.dialect == Dialects.SQL_SERVER:
|
|
90
|
-
from trilogy.dialect.sql_server import SqlServerDialect
|
|
91
|
-
|
|
92
|
-
self.generator = SqlServerDialect()
|
|
93
|
-
elif self.dialect == Dialects.DUCK_DB:
|
|
94
|
-
from trilogy.dialect.duckdb import DuckDBDialect
|
|
95
|
-
|
|
96
|
-
self.generator = DuckDBDialect()
|
|
97
|
-
elif self.dialect == Dialects.PRESTO:
|
|
98
|
-
from trilogy.dialect.presto import PrestoDialect
|
|
99
|
-
|
|
100
|
-
self.generator = PrestoDialect()
|
|
101
|
-
elif self.dialect == Dialects.TRINO:
|
|
102
|
-
from trilogy.dialect.presto import TrinoDialect
|
|
103
|
-
|
|
104
|
-
self.generator = TrinoDialect()
|
|
105
|
-
elif self.dialect == Dialects.POSTGRES:
|
|
106
|
-
from trilogy.dialect.postgres import PostgresDialect
|
|
107
|
-
|
|
108
|
-
self.generator = PostgresDialect()
|
|
109
|
-
elif self.dialect == Dialects.SNOWFLAKE:
|
|
110
|
-
from trilogy.dialect.snowflake import SnowflakeDialect
|
|
111
|
-
|
|
112
|
-
self.generator = SnowflakeDialect()
|
|
113
|
-
elif self.dialect == Dialects.DATAFRAME:
|
|
114
|
-
from trilogy.dialect.dataframe import DataframeDialect
|
|
115
|
-
|
|
116
|
-
self.generator = DataframeDialect()
|
|
117
|
-
else:
|
|
118
|
-
raise ValueError(f"Unsupported dialect {self.dialect}")
|
|
86
|
+
self.generator = get_dialect_generator(self.dialect)
|
|
119
87
|
self.connection = self.engine.connect()
|
|
120
88
|
# TODO: make generic
|
|
121
89
|
if self.dialect == Dialects.DATAFRAME:
|