pytrilogy 0.0.3.8__tar.gz → 0.0.3.10__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.8/pytrilogy.egg-info → pytrilogy-0.0.3.10}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/pytrilogy.egg-info/SOURCES.txt +1 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_select.py +6 -5
- pytrilogy-0.0.3.10/tests/test_typing.py +42 -0
- pytrilogy-0.0.3.10/tests/test_user_functions.py +206 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/authoring/__init__.py +6 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/enums.py +1 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/functions.py +9 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/models/author.py +65 -32
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/models/build.py +14 -1
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/models/core.py +13 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/models/environment.py +2 -1
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/statements/author.py +13 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/base.py +1 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/executor.py +5 -2
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/parsing/common.py +32 -9
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/parsing/parse_engine.py +24 -5
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/parsing/trilogy.lark +12 -8
- pytrilogy-0.0.3.8/tests/test_user_functions.py +0 -17
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/README.md +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/setup.cfg +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/setup.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/models/execute.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/optimizations/inline_constant.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.8 → pytrilogy-0.0.3.10}/trilogy/utility.py +0 -0
|
@@ -222,12 +222,14 @@ property id.class int;
|
|
|
222
222
|
property id.name string;
|
|
223
223
|
|
|
224
224
|
auto class_count <- count(class);
|
|
225
|
-
auto name_count <- count(name);
|
|
225
|
+
auto name_count <- count(name) by class;
|
|
226
|
+
|
|
227
|
+
auto test_ratio <- name_count / sum name_count over class;
|
|
226
228
|
|
|
227
229
|
select
|
|
228
230
|
class,
|
|
229
231
|
count(class) -> name_count,
|
|
230
|
-
|
|
232
|
+
name_count / sum name_count over class -> name_class_ratio,
|
|
231
233
|
;
|
|
232
234
|
"""
|
|
233
235
|
env, statements = env.parse(q1)
|
|
@@ -236,6 +238,5 @@ select
|
|
|
236
238
|
assert select.grain.components == {
|
|
237
239
|
"local.class",
|
|
238
240
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
241
|
+
benv = env.materialize_for_select()
|
|
242
|
+
assert benv.concepts["test_ratio"].keys == {"local.class", "local.id"}
|
|
@@ -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"]
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
from trilogy import Dialects
|
|
2
|
+
from trilogy.core.enums import Derivation, Purpose
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_user_function_def():
|
|
6
|
+
x = Dialects.DUCK_DB.default_executor()
|
|
7
|
+
|
|
8
|
+
results = x.execute_query(
|
|
9
|
+
"""
|
|
10
|
+
def percent_ratio(a, b, digits=3) -> round(a::float / b * 100, digits);
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
select @percent_ratio(10, 100) as ratio;
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
assert results.fetchall()[0].ratio == 10.0
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_user_function_aggregate():
|
|
22
|
+
x = Dialects.DUCK_DB.default_executor()
|
|
23
|
+
|
|
24
|
+
results = x.execute_query(
|
|
25
|
+
"""
|
|
26
|
+
key x int;
|
|
27
|
+
property x.price float;
|
|
28
|
+
|
|
29
|
+
datasource raw_data (
|
|
30
|
+
x: x,
|
|
31
|
+
price: price
|
|
32
|
+
)
|
|
33
|
+
grain (x)
|
|
34
|
+
query '''
|
|
35
|
+
select 1 as x, 2.0 as price
|
|
36
|
+
union all
|
|
37
|
+
select 2 as x, 3.0 as price
|
|
38
|
+
union all
|
|
39
|
+
select 10 as x, 5.0 as price
|
|
40
|
+
''';
|
|
41
|
+
|
|
42
|
+
def sum_times(a)-> a * sum(x);
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
select @sum_times(10) as total;
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
assert results.fetchall()[0].total == 130
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_user_function_nested():
|
|
54
|
+
x = Dialects.DUCK_DB.default_executor()
|
|
55
|
+
|
|
56
|
+
results = x.execute_query(
|
|
57
|
+
"""
|
|
58
|
+
key x int;
|
|
59
|
+
property x.price float;
|
|
60
|
+
|
|
61
|
+
datasource raw_data (
|
|
62
|
+
x: x,
|
|
63
|
+
price: price
|
|
64
|
+
)
|
|
65
|
+
grain (x)
|
|
66
|
+
query '''
|
|
67
|
+
select 1 as x, 2.0 as price
|
|
68
|
+
union all
|
|
69
|
+
select 2 as x, 3.0 as price
|
|
70
|
+
union all
|
|
71
|
+
select 10 as x, 5.0 as price
|
|
72
|
+
''';
|
|
73
|
+
|
|
74
|
+
def sum_times(a)-> a * sum(x + price);
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
select @sum_times(10) as total;
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
assert results.fetchall()[0].total == 230
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_user_function_nested_rowset():
|
|
86
|
+
x = Dialects.DUCK_DB.default_executor()
|
|
87
|
+
|
|
88
|
+
results = x.execute_query(
|
|
89
|
+
"""
|
|
90
|
+
key x int;
|
|
91
|
+
property x.price float;
|
|
92
|
+
|
|
93
|
+
datasource raw_data (
|
|
94
|
+
x: x,
|
|
95
|
+
price: price
|
|
96
|
+
)
|
|
97
|
+
grain (x)
|
|
98
|
+
query '''
|
|
99
|
+
select 1 as x, 2.0 as price
|
|
100
|
+
union all
|
|
101
|
+
select 2 as x, 3.0 as price
|
|
102
|
+
union all
|
|
103
|
+
select 10 as x, 5.0 as price
|
|
104
|
+
''';
|
|
105
|
+
|
|
106
|
+
def sum_times(a)-> a * sum(x + price);
|
|
107
|
+
|
|
108
|
+
with rowset as
|
|
109
|
+
select @sum_times(10) as total;
|
|
110
|
+
|
|
111
|
+
select rowset.total;
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
assert results.fetchall()[0].rowset_total == 230
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_user_function_case():
|
|
120
|
+
from trilogy.hooks.query_debugger import DebuggingHook
|
|
121
|
+
|
|
122
|
+
DebuggingHook()
|
|
123
|
+
x = Dialects.DUCK_DB.default_executor()
|
|
124
|
+
|
|
125
|
+
results = x.execute_query(
|
|
126
|
+
"""
|
|
127
|
+
key x int;
|
|
128
|
+
key y int;
|
|
129
|
+
property x.price float;
|
|
130
|
+
|
|
131
|
+
datasource raw_data (
|
|
132
|
+
x: x,
|
|
133
|
+
price: price
|
|
134
|
+
)
|
|
135
|
+
grain (x)
|
|
136
|
+
query '''
|
|
137
|
+
select 1 as x, 2.0 as price
|
|
138
|
+
union all
|
|
139
|
+
select 2 as x, 3.0 as price
|
|
140
|
+
union all
|
|
141
|
+
select 10 as x, 5.0 as price
|
|
142
|
+
''';
|
|
143
|
+
|
|
144
|
+
datasource join_x_y (
|
|
145
|
+
x: x,
|
|
146
|
+
y: y)
|
|
147
|
+
grain (x, y)
|
|
148
|
+
query '''
|
|
149
|
+
select 1 as x, 1 as y
|
|
150
|
+
union all
|
|
151
|
+
select 2 as x, 1 as y
|
|
152
|
+
union all
|
|
153
|
+
select 10 as x, 2 as y
|
|
154
|
+
''';
|
|
155
|
+
|
|
156
|
+
def weekday_sales(weekday) ->
|
|
157
|
+
SUM(CASE WHEN 10 = weekday THEN x ELSE 0 END) +
|
|
158
|
+
SUM(CASE WHEN 10 = weekday THEN price ELSE 0.0 END)
|
|
159
|
+
;
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
select
|
|
163
|
+
y,
|
|
164
|
+
@weekday_sales(10) -> test
|
|
165
|
+
order by y asc;
|
|
166
|
+
|
|
167
|
+
"""
|
|
168
|
+
)
|
|
169
|
+
results = results.fetchall()
|
|
170
|
+
assert results[0].test == 8
|
|
171
|
+
assert results[1].test == 15
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_parsing():
|
|
175
|
+
x = Dialects.DUCK_DB.default_executor()
|
|
176
|
+
x.execute_query(
|
|
177
|
+
"""
|
|
178
|
+
key x int;
|
|
179
|
+
property x.price float;
|
|
180
|
+
|
|
181
|
+
datasource raw_data (
|
|
182
|
+
x: x,
|
|
183
|
+
price: price
|
|
184
|
+
)
|
|
185
|
+
grain (x)
|
|
186
|
+
query '''
|
|
187
|
+
select 1 as x, 2.0 as price
|
|
188
|
+
union all
|
|
189
|
+
select 2 as x, 3.0 as price
|
|
190
|
+
union all
|
|
191
|
+
select 10 as x, 5.0 as price
|
|
192
|
+
''';
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
auto test <-SUM(CASE WHEN 10 = weekday THEN x ELSE 0 END) +
|
|
196
|
+
SUM(CASE WHEN 10 = weekday THEN price ELSE 0.0 END);
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
"""
|
|
202
|
+
)
|
|
203
|
+
test = x.environment.concepts["test"]
|
|
204
|
+
assert test.keys == set()
|
|
205
|
+
assert test.purpose == Purpose.METRIC
|
|
206
|
+
assert test.derivation == Derivation.BASIC
|
|
@@ -38,6 +38,9 @@ from trilogy.core.models.environment import Environment
|
|
|
38
38
|
from trilogy.core.statements.author import (
|
|
39
39
|
ConceptDeclarationStatement,
|
|
40
40
|
ConceptTransform,
|
|
41
|
+
MultiSelectStatement,
|
|
42
|
+
PersistStatement,
|
|
43
|
+
RawSQLStatement,
|
|
41
44
|
SelectItem,
|
|
42
45
|
SelectStatement,
|
|
43
46
|
)
|
|
@@ -87,4 +90,7 @@ __all__ = [
|
|
|
87
90
|
"DEFAULT_NAMESPACE",
|
|
88
91
|
"arbitrary_to_concept",
|
|
89
92
|
"arg_to_datatype",
|
|
93
|
+
"MultiSelectStatement",
|
|
94
|
+
"PersistStatement",
|
|
95
|
+
"RawSQLStatement",
|
|
90
96
|
]
|
|
@@ -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
|
|
@@ -547,6 +548,7 @@ class Comparison(ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
547
548
|
MagicConstants,
|
|
548
549
|
WindowItem,
|
|
549
550
|
AggregateWrapper,
|
|
551
|
+
FilterItem,
|
|
550
552
|
]
|
|
551
553
|
right: Union[
|
|
552
554
|
int,
|
|
@@ -566,6 +568,7 @@ class Comparison(ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
566
568
|
WindowItem,
|
|
567
569
|
AggregateWrapper,
|
|
568
570
|
TupleWrapper,
|
|
571
|
+
FilterItem,
|
|
569
572
|
]
|
|
570
573
|
operator: ComparisonOperator
|
|
571
574
|
|
|
@@ -754,7 +757,7 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
754
757
|
extra="forbid",
|
|
755
758
|
)
|
|
756
759
|
name: str
|
|
757
|
-
datatype: DataType | ListType | StructType | MapType | NumericType
|
|
760
|
+
datatype: DataType | TraitDataType | ListType | StructType | MapType | NumericType
|
|
758
761
|
purpose: Purpose
|
|
759
762
|
derivation: Derivation = Derivation.ROOT
|
|
760
763
|
granularity: Granularity = Granularity.MULTI_ROW
|
|
@@ -1215,9 +1218,9 @@ class UndefinedConceptFull(Concept, Mergeable, Namespaced):
|
|
|
1215
1218
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
1216
1219
|
name: str
|
|
1217
1220
|
line_no: int | None = None
|
|
1218
|
-
datatype:
|
|
1219
|
-
DataType
|
|
1220
|
-
)
|
|
1221
|
+
datatype: (
|
|
1222
|
+
DataType | TraitDataType | ListType | StructType | MapType | NumericType
|
|
1223
|
+
) = DataType.UNKNOWN
|
|
1221
1224
|
purpose: Purpose = Purpose.UNKNOWN
|
|
1222
1225
|
|
|
1223
1226
|
@property
|
|
@@ -1249,7 +1252,7 @@ class OrderItem(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
1249
1252
|
def with_merge(
|
|
1250
1253
|
self, source: Concept, target: Concept, modifiers: List[Modifier]
|
|
1251
1254
|
) -> "OrderItem":
|
|
1252
|
-
return OrderItem(
|
|
1255
|
+
return OrderItem.model_construct(
|
|
1253
1256
|
expr=(
|
|
1254
1257
|
self.expr.with_merge(source, target, modifiers)
|
|
1255
1258
|
if isinstance(self.expr, Mergeable)
|
|
@@ -1258,6 +1261,16 @@ class OrderItem(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
1258
1261
|
order=self.order,
|
|
1259
1262
|
)
|
|
1260
1263
|
|
|
1264
|
+
def with_reference_replacement(self, source, target):
|
|
1265
|
+
return OrderItem.model_construct(
|
|
1266
|
+
expr=(
|
|
1267
|
+
self.expr.with_reference_replacement(source, target)
|
|
1268
|
+
if isinstance(self.expr, Mergeable)
|
|
1269
|
+
else self.expr
|
|
1270
|
+
),
|
|
1271
|
+
order=self.order,
|
|
1272
|
+
)
|
|
1273
|
+
|
|
1261
1274
|
@property
|
|
1262
1275
|
def concept_arguments(self) -> Sequence[ConceptRef]:
|
|
1263
1276
|
return get_concept_arguments(self.expr)
|
|
@@ -1320,6 +1333,17 @@ class WindowItem(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1320
1333
|
)
|
|
1321
1334
|
return output
|
|
1322
1335
|
|
|
1336
|
+
def with_reference_replacement(self, source, target):
|
|
1337
|
+
return WindowItem.model_construct(
|
|
1338
|
+
type=self.type,
|
|
1339
|
+
content=self.content.with_reference_replacement(source, target),
|
|
1340
|
+
over=[x.with_reference_replacement(source, target) for x in self.over],
|
|
1341
|
+
order_by=[
|
|
1342
|
+
x.with_reference_replacement(source, target) for x in self.order_by
|
|
1343
|
+
],
|
|
1344
|
+
index=self.index,
|
|
1345
|
+
)
|
|
1346
|
+
|
|
1323
1347
|
def with_namespace(self, namespace: str) -> "WindowItem":
|
|
1324
1348
|
return WindowItem.model_construct(
|
|
1325
1349
|
type=self.type,
|
|
@@ -1515,30 +1539,7 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1515
1539
|
List[Set[DataType]],
|
|
1516
1540
|
]
|
|
1517
1541
|
] = None
|
|
1518
|
-
arguments: Sequence[
|
|
1519
|
-
Union[
|
|
1520
|
-
ConceptRef,
|
|
1521
|
-
AggregateWrapper,
|
|
1522
|
-
Function,
|
|
1523
|
-
Parenthetical,
|
|
1524
|
-
CaseWhen,
|
|
1525
|
-
CaseElse,
|
|
1526
|
-
WindowItem,
|
|
1527
|
-
int,
|
|
1528
|
-
float,
|
|
1529
|
-
str,
|
|
1530
|
-
date,
|
|
1531
|
-
datetime,
|
|
1532
|
-
MapWrapper[Any, Any],
|
|
1533
|
-
DataType,
|
|
1534
|
-
ListType,
|
|
1535
|
-
MapType,
|
|
1536
|
-
NumericType,
|
|
1537
|
-
DatePart,
|
|
1538
|
-
list,
|
|
1539
|
-
ListWrapper[Any],
|
|
1540
|
-
]
|
|
1541
|
-
]
|
|
1542
|
+
arguments: Sequence[FuncArgs]
|
|
1542
1543
|
|
|
1543
1544
|
def __init__(self, **kwargs):
|
|
1544
1545
|
super().__init__(**kwargs)
|
|
@@ -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
|
|
@@ -2164,6 +2170,7 @@ Expr = (
|
|
|
2164
2170
|
| datetime
|
|
2165
2171
|
| TupleWrapper
|
|
2166
2172
|
| ListWrapper
|
|
2173
|
+
| MapWrapper
|
|
2167
2174
|
| WindowItem
|
|
2168
2175
|
| FilterItem
|
|
2169
2176
|
| ConceptRef
|
|
@@ -2172,4 +2179,30 @@ Expr = (
|
|
|
2172
2179
|
| Parenthetical
|
|
2173
2180
|
| Function
|
|
2174
2181
|
| AggregateWrapper
|
|
2182
|
+
| CaseWhen
|
|
2183
|
+
| CaseElse
|
|
2184
|
+
)
|
|
2185
|
+
|
|
2186
|
+
FuncArgs = (
|
|
2187
|
+
ConceptRef
|
|
2188
|
+
| AggregateWrapper
|
|
2189
|
+
| Function
|
|
2190
|
+
| Parenthetical
|
|
2191
|
+
| CaseWhen
|
|
2192
|
+
| CaseElse
|
|
2193
|
+
| WindowItem
|
|
2194
|
+
| FilterItem
|
|
2195
|
+
| int
|
|
2196
|
+
| float
|
|
2197
|
+
| str
|
|
2198
|
+
| date
|
|
2199
|
+
| datetime
|
|
2200
|
+
| MapWrapper[Any, Any]
|
|
2201
|
+
| DataType
|
|
2202
|
+
| ListType
|
|
2203
|
+
| MapType
|
|
2204
|
+
| NumericType
|
|
2205
|
+
| DatePart
|
|
2206
|
+
| list
|
|
2207
|
+
| ListWrapper[Any]
|
|
2175
2208
|
)
|
|
@@ -51,6 +51,7 @@ from trilogy.core.models.author import (
|
|
|
51
51
|
ConceptRef,
|
|
52
52
|
Conditional,
|
|
53
53
|
FilterItem,
|
|
54
|
+
FuncArgs,
|
|
54
55
|
Function,
|
|
55
56
|
Grain,
|
|
56
57
|
HavingClause,
|
|
@@ -1498,10 +1499,22 @@ class Factory:
|
|
|
1498
1499
|
|
|
1499
1500
|
@build.register
|
|
1500
1501
|
def _(self, base: Function) -> BuildFunction:
|
|
1502
|
+
from trilogy.parsing.common import arbitrary_to_concept
|
|
1501
1503
|
|
|
1504
|
+
raw_args: list[Concept | FuncArgs] = []
|
|
1505
|
+
for arg in base.arguments:
|
|
1506
|
+
# to do proper discovery, we need to inject virtual intermediate ocncepts
|
|
1507
|
+
if isinstance(arg, (AggregateWrapper, FilterItem, WindowItem)):
|
|
1508
|
+
narg = arbitrary_to_concept(
|
|
1509
|
+
arg,
|
|
1510
|
+
environment=self.environment,
|
|
1511
|
+
)
|
|
1512
|
+
raw_args.append(narg)
|
|
1513
|
+
else:
|
|
1514
|
+
raw_args.append(arg)
|
|
1502
1515
|
new = BuildFunction.model_construct(
|
|
1503
1516
|
operator=base.operator,
|
|
1504
|
-
arguments=[self.build(c) for c in
|
|
1517
|
+
arguments=[self.build(c) for c in raw_args],
|
|
1505
1518
|
output_datatype=base.output_datatype,
|
|
1506
1519
|
output_purpose=base.output_purpose,
|
|
1507
1520
|
valid_inputs=base.valid_inputs,
|
|
@@ -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,
|
|
@@ -191,7 +192,7 @@ class Environment(BaseModel):
|
|
|
191
192
|
EnvironmentDatasourceDict, PlainValidator(validate_datasources)
|
|
192
193
|
] = Field(default_factory=EnvironmentDatasourceDict)
|
|
193
194
|
functions: Dict[str, Callable[..., Any]] = Field(default_factory=dict)
|
|
194
|
-
data_types: Dict[str,
|
|
195
|
+
data_types: Dict[str, CustomType] = Field(default_factory=dict)
|
|
195
196
|
named_statements: Dict[str, SelectLineage] = Field(default_factory=dict)
|
|
196
197
|
imports: Dict[str, list[Import]] = Field(
|
|
197
198
|
default_factory=lambda: defaultdict(list) # type: ignore
|
|
@@ -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]})",
|
|
@@ -166,8 +166,11 @@ class Executor(object):
|
|
|
166
166
|
)
|
|
167
167
|
|
|
168
168
|
@execute_query.register
|
|
169
|
-
def _(self, query: str) -> CursorResult:
|
|
170
|
-
|
|
169
|
+
def _(self, query: str) -> CursorResult | None:
|
|
170
|
+
results = self.execute_text(query)
|
|
171
|
+
if results:
|
|
172
|
+
return results[-1]
|
|
173
|
+
return None
|
|
171
174
|
|
|
172
175
|
@execute_query.register
|
|
173
176
|
def _(self, query: SelectStatement) -> CursorResult:
|