pytrilogy 0.0.3.7__tar.gz → 0.0.3.9__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.7/pytrilogy.egg-info → pytrilogy-0.0.3.9}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/pytrilogy.egg-info/SOURCES.txt +1 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_models.py +6 -2
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_select.py +6 -5
- pytrilogy-0.0.3.9/tests/test_user_functions.py +206 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/models/author.py +155 -63
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/models/build.py +35 -5
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/models/environment.py +2 -1
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/group_node.py +1 -1
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/multiselect_node.py +1 -1
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/statements/author.py +2 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/config.py +8 -2
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/dataframe.py +9 -4
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/executor.py +5 -2
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/parsing/common.py +88 -56
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/parsing/parse_engine.py +53 -33
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/parsing/trilogy.lark +6 -4
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/README.md +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/setup.cfg +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/setup.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/authoring/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/enums.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/functions.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/models/execute.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/optimizations/inline_constant.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/base.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.7 → pytrilogy-0.0.3.9}/trilogy/utility.py +0 -0
|
@@ -219,10 +219,14 @@ def test_comparison():
|
|
|
219
219
|
# this should not error
|
|
220
220
|
Comparison(left=1, right=[1, 2, 3], operator=ComparisonOperator.IN)
|
|
221
221
|
|
|
222
|
-
Comparison(
|
|
222
|
+
Comparison(
|
|
223
|
+
left=1,
|
|
224
|
+
right=ListWrapper([1, 2, 3], type=DataType.INTEGER),
|
|
225
|
+
operator=ComparisonOperator.IN,
|
|
226
|
+
)
|
|
223
227
|
|
|
224
228
|
|
|
225
|
-
def
|
|
229
|
+
def test_comparison_invalid_type():
|
|
226
230
|
try:
|
|
227
231
|
Comparison(left=1, right="abc", operator=ComparisonOperator.EQ)
|
|
228
232
|
except Exception as exc:
|
|
@@ -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,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
|
|
@@ -75,6 +75,9 @@ class Mergeable(ABC):
|
|
|
75
75
|
def with_merge(self, source: Concept, target: Concept, modifiers: List[Modifier]):
|
|
76
76
|
raise NotImplementedError
|
|
77
77
|
|
|
78
|
+
def with_reference_replacement(self, source: str, target: Expr):
|
|
79
|
+
raise NotImplementedError(type(self))
|
|
80
|
+
|
|
78
81
|
|
|
79
82
|
class ConceptArgs(ABC):
|
|
80
83
|
@property
|
|
@@ -152,6 +155,11 @@ class ConceptRef(Addressable, Namespaced, DataTyped, Mergeable, BaseModel):
|
|
|
152
155
|
metadata=self.metadata,
|
|
153
156
|
)
|
|
154
157
|
|
|
158
|
+
def with_reference_replacement(self, source: str, target: Expr):
|
|
159
|
+
if self.address == source:
|
|
160
|
+
return target
|
|
161
|
+
return self
|
|
162
|
+
|
|
155
163
|
|
|
156
164
|
class UndefinedConcept(ConceptRef):
|
|
157
165
|
pass
|
|
@@ -409,7 +417,7 @@ class Grain(Namespaced, BaseModel):
|
|
|
409
417
|
) -> Grain:
|
|
410
418
|
from trilogy.parsing.common import concepts_to_grain_concepts
|
|
411
419
|
|
|
412
|
-
|
|
420
|
+
x = Grain.model_construct(
|
|
413
421
|
components={
|
|
414
422
|
c.address
|
|
415
423
|
for c in concepts_to_grain_concepts(concepts, environment=environment)
|
|
@@ -417,6 +425,8 @@ class Grain(Namespaced, BaseModel):
|
|
|
417
425
|
where_clause=where_clause,
|
|
418
426
|
)
|
|
419
427
|
|
|
428
|
+
return x
|
|
429
|
+
|
|
420
430
|
def with_namespace(self, namespace: str) -> "Grain":
|
|
421
431
|
return Grain.model_construct(
|
|
422
432
|
components={address_with_namespace(c, namespace) for c in self.components},
|
|
@@ -537,6 +547,7 @@ class Comparison(ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
537
547
|
MagicConstants,
|
|
538
548
|
WindowItem,
|
|
539
549
|
AggregateWrapper,
|
|
550
|
+
FilterItem,
|
|
540
551
|
]
|
|
541
552
|
right: Union[
|
|
542
553
|
int,
|
|
@@ -556,6 +567,7 @@ class Comparison(ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
556
567
|
WindowItem,
|
|
557
568
|
AggregateWrapper,
|
|
558
569
|
TupleWrapper,
|
|
570
|
+
FilterItem,
|
|
559
571
|
]
|
|
560
572
|
operator: ComparisonOperator
|
|
561
573
|
|
|
@@ -662,6 +674,21 @@ class Comparison(ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
662
674
|
operator=self.operator,
|
|
663
675
|
)
|
|
664
676
|
|
|
677
|
+
def with_reference_replacement(self, source, target):
|
|
678
|
+
return self.__class__.model_construct(
|
|
679
|
+
left=(
|
|
680
|
+
self.left.with_reference_replacement(source, target)
|
|
681
|
+
if isinstance(self.left, Mergeable)
|
|
682
|
+
else self.left
|
|
683
|
+
),
|
|
684
|
+
right=(
|
|
685
|
+
self.right.with_reference_replacement(source, target)
|
|
686
|
+
if isinstance(self.right, Mergeable)
|
|
687
|
+
else self.right
|
|
688
|
+
),
|
|
689
|
+
operator=self.operator,
|
|
690
|
+
)
|
|
691
|
+
|
|
665
692
|
def with_namespace(self, namespace: str):
|
|
666
693
|
return self.__class__.model_construct(
|
|
667
694
|
left=(
|
|
@@ -1224,7 +1251,7 @@ class OrderItem(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
1224
1251
|
def with_merge(
|
|
1225
1252
|
self, source: Concept, target: Concept, modifiers: List[Modifier]
|
|
1226
1253
|
) -> "OrderItem":
|
|
1227
|
-
return OrderItem(
|
|
1254
|
+
return OrderItem.model_construct(
|
|
1228
1255
|
expr=(
|
|
1229
1256
|
self.expr.with_merge(source, target, modifiers)
|
|
1230
1257
|
if isinstance(self.expr, Mergeable)
|
|
@@ -1233,19 +1260,19 @@ class OrderItem(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
1233
1260
|
order=self.order,
|
|
1234
1261
|
)
|
|
1235
1262
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1263
|
+
def with_reference_replacement(self, source, target):
|
|
1264
|
+
return OrderItem.model_construct(
|
|
1265
|
+
expr=(
|
|
1266
|
+
self.expr.with_reference_replacement(source, target)
|
|
1267
|
+
if isinstance(self.expr, Mergeable)
|
|
1268
|
+
else self.expr
|
|
1269
|
+
),
|
|
1270
|
+
order=self.order,
|
|
1271
|
+
)
|
|
1239
1272
|
|
|
1240
1273
|
@property
|
|
1241
1274
|
def concept_arguments(self) -> Sequence[ConceptRef]:
|
|
1242
|
-
|
|
1243
|
-
x = self.expr
|
|
1244
|
-
if isinstance(x, ConceptRef):
|
|
1245
|
-
base += [x]
|
|
1246
|
-
elif isinstance(x, ConceptArgs):
|
|
1247
|
-
base += x.concept_arguments
|
|
1248
|
-
return base
|
|
1275
|
+
return get_concept_arguments(self.expr)
|
|
1249
1276
|
|
|
1250
1277
|
@property
|
|
1251
1278
|
def row_arguments(self) -> Sequence[ConceptRef]:
|
|
@@ -1305,6 +1332,17 @@ class WindowItem(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1305
1332
|
)
|
|
1306
1333
|
return output
|
|
1307
1334
|
|
|
1335
|
+
def with_reference_replacement(self, source, target):
|
|
1336
|
+
return WindowItem.model_construct(
|
|
1337
|
+
type=self.type,
|
|
1338
|
+
content=self.content.with_reference_replacement(source, target),
|
|
1339
|
+
over=[x.with_reference_replacement(source, target) for x in self.over],
|
|
1340
|
+
order_by=[
|
|
1341
|
+
x.with_reference_replacement(source, target) for x in self.order_by
|
|
1342
|
+
],
|
|
1343
|
+
index=self.index,
|
|
1344
|
+
)
|
|
1345
|
+
|
|
1308
1346
|
def with_namespace(self, namespace: str) -> "WindowItem":
|
|
1309
1347
|
return WindowItem.model_construct(
|
|
1310
1348
|
type=self.type,
|
|
@@ -1316,31 +1354,19 @@ class WindowItem(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1316
1354
|
|
|
1317
1355
|
@property
|
|
1318
1356
|
def concept_arguments(self) -> List[ConceptRef]:
|
|
1319
|
-
return self.arguments
|
|
1320
|
-
|
|
1321
|
-
@property
|
|
1322
|
-
def arguments(self) -> List[ConceptRef]:
|
|
1323
1357
|
output = [self.content]
|
|
1324
1358
|
for order in self.order_by:
|
|
1325
|
-
output +=
|
|
1359
|
+
output += get_concept_arguments(order)
|
|
1326
1360
|
for item in self.over:
|
|
1327
|
-
output +=
|
|
1361
|
+
output += get_concept_arguments(item)
|
|
1328
1362
|
return output
|
|
1329
1363
|
|
|
1330
|
-
@property
|
|
1331
|
-
def output(self) -> ConceptRef:
|
|
1332
|
-
return self.content
|
|
1333
|
-
|
|
1334
1364
|
@property
|
|
1335
1365
|
def output_datatype(self):
|
|
1336
1366
|
if self.type in (WindowType.RANK, WindowType.ROW_NUMBER):
|
|
1337
1367
|
return DataType.INTEGER
|
|
1338
1368
|
return self.content.output_datatype
|
|
1339
1369
|
|
|
1340
|
-
@property
|
|
1341
|
-
def output_purpose(self):
|
|
1342
|
-
return Purpose.PROPERTY
|
|
1343
|
-
|
|
1344
1370
|
|
|
1345
1371
|
def get_basic_type(
|
|
1346
1372
|
type: DataType | ListType | StructType | MapType | NumericType,
|
|
@@ -1407,12 +1433,28 @@ class CaseWhen(Namespaced, ConceptArgs, Mergeable, BaseModel):
|
|
|
1407
1433
|
),
|
|
1408
1434
|
)
|
|
1409
1435
|
|
|
1436
|
+
def with_reference_replacement(self, source, target):
|
|
1437
|
+
return CaseWhen.model_construct(
|
|
1438
|
+
comparison=self.comparison.with_reference_replacement(source, target),
|
|
1439
|
+
expr=(
|
|
1440
|
+
self.expr.with_reference_replacement(source, target)
|
|
1441
|
+
if isinstance(self.expr, Mergeable)
|
|
1442
|
+
else self.expr
|
|
1443
|
+
),
|
|
1444
|
+
)
|
|
1445
|
+
|
|
1410
1446
|
|
|
1411
1447
|
class CaseElse(Namespaced, ConceptArgs, Mergeable, BaseModel):
|
|
1412
1448
|
expr: "Expr"
|
|
1413
1449
|
# this ensures that it's easily differentiable from CaseWhen
|
|
1414
1450
|
discriminant: ComparisonOperator = ComparisonOperator.ELSE
|
|
1415
1451
|
|
|
1452
|
+
def __str__(self):
|
|
1453
|
+
return self.__repr__()
|
|
1454
|
+
|
|
1455
|
+
def __repr__(self):
|
|
1456
|
+
return f"ELSE {str(self.expr)}"
|
|
1457
|
+
|
|
1416
1458
|
@field_validator("expr", mode="before")
|
|
1417
1459
|
def enforce_expr(cls, v):
|
|
1418
1460
|
if isinstance(v, Concept):
|
|
@@ -1435,6 +1477,19 @@ class CaseElse(Namespaced, ConceptArgs, Mergeable, BaseModel):
|
|
|
1435
1477
|
),
|
|
1436
1478
|
)
|
|
1437
1479
|
|
|
1480
|
+
def with_reference_replacement(self, source, target):
|
|
1481
|
+
return CaseElse.model_construct(
|
|
1482
|
+
discriminant=self.discriminant,
|
|
1483
|
+
expr=(
|
|
1484
|
+
self.expr.with_reference_replacement(
|
|
1485
|
+
source,
|
|
1486
|
+
target,
|
|
1487
|
+
)
|
|
1488
|
+
if isinstance(self.expr, Mergeable)
|
|
1489
|
+
else self.expr
|
|
1490
|
+
),
|
|
1491
|
+
)
|
|
1492
|
+
|
|
1438
1493
|
def with_namespace(self, namespace: str) -> CaseElse:
|
|
1439
1494
|
return CaseElse.model_construct(
|
|
1440
1495
|
discriminant=self.discriminant,
|
|
@@ -1483,35 +1538,10 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1483
1538
|
List[Set[DataType]],
|
|
1484
1539
|
]
|
|
1485
1540
|
] = None
|
|
1486
|
-
arguments: Sequence[
|
|
1487
|
-
Union[
|
|
1488
|
-
ConceptRef,
|
|
1489
|
-
AggregateWrapper,
|
|
1490
|
-
Function,
|
|
1491
|
-
Parenthetical,
|
|
1492
|
-
CaseWhen,
|
|
1493
|
-
CaseElse,
|
|
1494
|
-
WindowItem,
|
|
1495
|
-
int,
|
|
1496
|
-
float,
|
|
1497
|
-
str,
|
|
1498
|
-
date,
|
|
1499
|
-
datetime,
|
|
1500
|
-
MapWrapper[Any, Any],
|
|
1501
|
-
DataType,
|
|
1502
|
-
ListType,
|
|
1503
|
-
MapType,
|
|
1504
|
-
NumericType,
|
|
1505
|
-
DatePart,
|
|
1506
|
-
list,
|
|
1507
|
-
ListWrapper[Any],
|
|
1508
|
-
]
|
|
1509
|
-
]
|
|
1541
|
+
arguments: Sequence[FuncArgs]
|
|
1510
1542
|
|
|
1511
1543
|
def __init__(self, **kwargs):
|
|
1512
1544
|
super().__init__(**kwargs)
|
|
1513
|
-
if "datatype" in str(self):
|
|
1514
|
-
raise SyntaxError(str(self))
|
|
1515
1545
|
|
|
1516
1546
|
def __repr__(self):
|
|
1517
1547
|
return f'{self.operator.value}({",".join([str(a) for a in self.arguments])})'
|
|
@@ -1597,6 +1627,29 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1597
1627
|
)
|
|
1598
1628
|
return v
|
|
1599
1629
|
|
|
1630
|
+
def with_reference_replacement(self, source: str, target: Expr):
|
|
1631
|
+
return Function.model_construct(
|
|
1632
|
+
operator=self.operator,
|
|
1633
|
+
arguments=[
|
|
1634
|
+
(
|
|
1635
|
+
c.with_reference_replacement(
|
|
1636
|
+
source,
|
|
1637
|
+
target,
|
|
1638
|
+
)
|
|
1639
|
+
if isinstance(
|
|
1640
|
+
c,
|
|
1641
|
+
Mergeable,
|
|
1642
|
+
)
|
|
1643
|
+
else c
|
|
1644
|
+
)
|
|
1645
|
+
for c in self.arguments
|
|
1646
|
+
],
|
|
1647
|
+
output_datatype=self.output_datatype,
|
|
1648
|
+
output_purpose=self.output_purpose,
|
|
1649
|
+
valid_inputs=self.valid_inputs,
|
|
1650
|
+
arg_count=self.arg_count,
|
|
1651
|
+
)
|
|
1652
|
+
|
|
1600
1653
|
def with_namespace(self, namespace: str) -> "Function":
|
|
1601
1654
|
return Function.model_construct(
|
|
1602
1655
|
operator=self.operator,
|
|
@@ -1658,10 +1711,13 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1658
1711
|
return base_grain
|
|
1659
1712
|
|
|
1660
1713
|
|
|
1661
|
-
class AggregateWrapper(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
1714
|
+
class AggregateWrapper(Mergeable, DataTyped, ConceptArgs, Namespaced, BaseModel):
|
|
1662
1715
|
function: Function
|
|
1663
1716
|
by: List[ConceptRef] = Field(default_factory=list)
|
|
1664
1717
|
|
|
1718
|
+
def __init__(self, **kwargs):
|
|
1719
|
+
super().__init__(**kwargs)
|
|
1720
|
+
|
|
1665
1721
|
@field_validator("by", mode="before")
|
|
1666
1722
|
@classmethod
|
|
1667
1723
|
def enforce_concept_ref(cls, v):
|
|
@@ -1693,10 +1749,6 @@ class AggregateWrapper(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
1693
1749
|
def output_purpose(self):
|
|
1694
1750
|
return self.function.output_purpose
|
|
1695
1751
|
|
|
1696
|
-
@property
|
|
1697
|
-
def arguments(self):
|
|
1698
|
-
return self.function.arguments
|
|
1699
|
-
|
|
1700
1752
|
def with_merge(self, source: Concept, target: Concept, modifiers: List[Modifier]):
|
|
1701
1753
|
return AggregateWrapper.model_construct(
|
|
1702
1754
|
function=self.function.with_merge(source, target, modifiers=modifiers),
|
|
@@ -1707,6 +1759,16 @@ class AggregateWrapper(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
1707
1759
|
),
|
|
1708
1760
|
)
|
|
1709
1761
|
|
|
1762
|
+
def with_reference_replacement(self, source, target):
|
|
1763
|
+
return AggregateWrapper.model_construct(
|
|
1764
|
+
function=self.function.with_reference_replacement(source, target),
|
|
1765
|
+
by=(
|
|
1766
|
+
[c.with_reference_replacement(source, target) for c in self.by]
|
|
1767
|
+
if self.by
|
|
1768
|
+
else []
|
|
1769
|
+
),
|
|
1770
|
+
)
|
|
1771
|
+
|
|
1710
1772
|
def with_namespace(self, namespace: str) -> "AggregateWrapper":
|
|
1711
1773
|
return AggregateWrapper.model_construct(
|
|
1712
1774
|
function=self.function.with_namespace(namespace),
|
|
@@ -1796,11 +1858,6 @@ class RowsetItem(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
1796
1858
|
rowset=self.rowset.with_namespace(namespace),
|
|
1797
1859
|
)
|
|
1798
1860
|
|
|
1799
|
-
@property
|
|
1800
|
-
def arguments(self) -> List[ConceptRef]:
|
|
1801
|
-
output = [self.content]
|
|
1802
|
-
return output
|
|
1803
|
-
|
|
1804
1861
|
@property
|
|
1805
1862
|
def output(self) -> ConceptRef:
|
|
1806
1863
|
return self.content
|
|
@@ -1831,7 +1888,10 @@ class OrderBy(Mergeable, Namespaced, BaseModel):
|
|
|
1831
1888
|
|
|
1832
1889
|
@property
|
|
1833
1890
|
def concept_arguments(self):
|
|
1834
|
-
|
|
1891
|
+
base = []
|
|
1892
|
+
for x in self.items:
|
|
1893
|
+
base += x.concept_arguments
|
|
1894
|
+
return base
|
|
1835
1895
|
|
|
1836
1896
|
|
|
1837
1897
|
class AlignClause(Namespaced, BaseModel):
|
|
@@ -2088,6 +2148,11 @@ class Comment(BaseModel):
|
|
|
2088
2148
|
text: str
|
|
2089
2149
|
|
|
2090
2150
|
|
|
2151
|
+
class ArgBinding(BaseModel):
|
|
2152
|
+
name: str
|
|
2153
|
+
default: Expr | None = None
|
|
2154
|
+
|
|
2155
|
+
|
|
2091
2156
|
Expr = (
|
|
2092
2157
|
MagicConstants
|
|
2093
2158
|
| bool
|
|
@@ -2099,6 +2164,7 @@ Expr = (
|
|
|
2099
2164
|
| datetime
|
|
2100
2165
|
| TupleWrapper
|
|
2101
2166
|
| ListWrapper
|
|
2167
|
+
| MapWrapper
|
|
2102
2168
|
| WindowItem
|
|
2103
2169
|
| FilterItem
|
|
2104
2170
|
| ConceptRef
|
|
@@ -2107,4 +2173,30 @@ Expr = (
|
|
|
2107
2173
|
| Parenthetical
|
|
2108
2174
|
| Function
|
|
2109
2175
|
| AggregateWrapper
|
|
2176
|
+
| CaseWhen
|
|
2177
|
+
| CaseElse
|
|
2178
|
+
)
|
|
2179
|
+
|
|
2180
|
+
FuncArgs = (
|
|
2181
|
+
ConceptRef
|
|
2182
|
+
| AggregateWrapper
|
|
2183
|
+
| Function
|
|
2184
|
+
| Parenthetical
|
|
2185
|
+
| CaseWhen
|
|
2186
|
+
| CaseElse
|
|
2187
|
+
| WindowItem
|
|
2188
|
+
| FilterItem
|
|
2189
|
+
| int
|
|
2190
|
+
| float
|
|
2191
|
+
| str
|
|
2192
|
+
| date
|
|
2193
|
+
| datetime
|
|
2194
|
+
| MapWrapper[Any, Any]
|
|
2195
|
+
| DataType
|
|
2196
|
+
| ListType
|
|
2197
|
+
| MapType
|
|
2198
|
+
| NumericType
|
|
2199
|
+
| DatePart
|
|
2200
|
+
| list
|
|
2201
|
+
| ListWrapper[Any]
|
|
2110
2202
|
)
|