pytrilogy 0.0.3.47__tar.gz → 0.0.3.51__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.47/pytrilogy.egg-info → pytrilogy-0.0.3.51}/PKG-INFO +12 -15
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/README.md +11 -14
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51/pytrilogy.egg-info}/PKG-INFO +12 -15
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/pytrilogy.egg-info/SOURCES.txt +0 -1
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_functions.py +4 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_parse_engine.py +23 -1
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/enums.py +3 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/functions.py +22 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/models/author.py +6 -2
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/models/build.py +42 -8
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/models/execute.py +6 -3
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/optimization.py +0 -3
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/optimizations/__init__.py +0 -2
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/concept_strategies_v3.py +1 -1
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/common.py +3 -4
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/filter_node.py +142 -91
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/group_node.py +7 -5
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/base_node.py +4 -1
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/query_processor.py +27 -12
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/statements/author.py +0 -2
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/base.py +28 -6
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/bigquery.py +2 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/common.py +25 -14
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/duckdb.py +2 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/executor.py +3 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/parsing/common.py +25 -16
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/parsing/parse_engine.py +36 -8
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/parsing/trilogy.lark +10 -4
- pytrilogy-0.0.3.47/trilogy/core/optimizations/inline_constant.py +0 -35
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/setup.cfg +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/setup.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_failure.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_parsing_failures.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/authoring/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/models/environment.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/std/date.preql +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/std/geography.preql +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/std/money.preql +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.0.3.47 → pytrilogy-0.0.3.51}/trilogy/utility.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.3.
|
|
3
|
+
Version: 0.0.3.51
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -53,7 +53,7 @@ Installation: `pip install pytrilogy`
|
|
|
53
53
|
|
|
54
54
|
You can read more about the project [here](https://trilogydata.dev/) and try out an interactive demo [here](https://trilogydata.dev/demo/).
|
|
55
55
|
|
|
56
|
-
Trilogy:
|
|
56
|
+
Trilogy looks like SQL:
|
|
57
57
|
```sql
|
|
58
58
|
WHERE
|
|
59
59
|
name like '%lvis%'
|
|
@@ -65,7 +65,7 @@ ORDER BY
|
|
|
65
65
|
LIMIT 10;
|
|
66
66
|
```
|
|
67
67
|
## Goals
|
|
68
|
-
|
|
68
|
+
And aims to:
|
|
69
69
|
|
|
70
70
|
Preserve:
|
|
71
71
|
- Correctness
|
|
@@ -85,6 +85,7 @@ Maintain:
|
|
|
85
85
|
Save the following code in a file named `hello.preql`
|
|
86
86
|
|
|
87
87
|
```python
|
|
88
|
+
# semantic model is abstract from data
|
|
88
89
|
key sentence_id int;
|
|
89
90
|
property sentence_id.word_one string; # comments after a definition
|
|
90
91
|
property sentence_id.word_two string; # are syntactic sugar for adding
|
|
@@ -92,7 +93,8 @@ property sentence_id.word_three string; # a description to it
|
|
|
92
93
|
|
|
93
94
|
# comments in other places are just comments
|
|
94
95
|
|
|
95
|
-
# define our
|
|
96
|
+
# define our datasource to bind the model to data
|
|
97
|
+
# testing using query fixtures is a common pattern
|
|
96
98
|
datasource word_one(
|
|
97
99
|
sentence: sentence_id,
|
|
98
100
|
word:word_one
|
|
@@ -126,25 +128,20 @@ union all
|
|
|
126
128
|
select 2 as sentence, '!'
|
|
127
129
|
''';
|
|
128
130
|
|
|
131
|
+
def concat_with_space(x,y) -> x || ' ' || y;
|
|
132
|
+
|
|
129
133
|
# an actual select statement
|
|
130
134
|
# joins are automatically resolved between the 3 sources
|
|
131
135
|
with sentences as
|
|
132
|
-
select sentence_id, word_one
|
|
136
|
+
select sentence_id, @concat_with_space(word_one, word_two) || word_three as text;
|
|
133
137
|
|
|
134
|
-
SELECT
|
|
135
|
-
--sentences.sentence_id,
|
|
136
|
-
sentences.text
|
|
137
138
|
WHERE
|
|
138
|
-
sentences.sentence_id
|
|
139
|
-
;
|
|
140
|
-
|
|
139
|
+
sentences.sentence_id in (1,2)
|
|
141
140
|
SELECT
|
|
142
|
-
--sentences.sentence_id,
|
|
143
141
|
sentences.text
|
|
144
|
-
WHERE
|
|
145
|
-
sentences.sentence_id = 2
|
|
146
142
|
;
|
|
147
|
-
|
|
143
|
+
|
|
144
|
+
|
|
148
145
|
|
|
149
146
|
```
|
|
150
147
|
|
|
@@ -15,7 +15,7 @@ Installation: `pip install pytrilogy`
|
|
|
15
15
|
|
|
16
16
|
You can read more about the project [here](https://trilogydata.dev/) and try out an interactive demo [here](https://trilogydata.dev/demo/).
|
|
17
17
|
|
|
18
|
-
Trilogy:
|
|
18
|
+
Trilogy looks like SQL:
|
|
19
19
|
```sql
|
|
20
20
|
WHERE
|
|
21
21
|
name like '%lvis%'
|
|
@@ -27,7 +27,7 @@ ORDER BY
|
|
|
27
27
|
LIMIT 10;
|
|
28
28
|
```
|
|
29
29
|
## Goals
|
|
30
|
-
|
|
30
|
+
And aims to:
|
|
31
31
|
|
|
32
32
|
Preserve:
|
|
33
33
|
- Correctness
|
|
@@ -47,6 +47,7 @@ Maintain:
|
|
|
47
47
|
Save the following code in a file named `hello.preql`
|
|
48
48
|
|
|
49
49
|
```python
|
|
50
|
+
# semantic model is abstract from data
|
|
50
51
|
key sentence_id int;
|
|
51
52
|
property sentence_id.word_one string; # comments after a definition
|
|
52
53
|
property sentence_id.word_two string; # are syntactic sugar for adding
|
|
@@ -54,7 +55,8 @@ property sentence_id.word_three string; # a description to it
|
|
|
54
55
|
|
|
55
56
|
# comments in other places are just comments
|
|
56
57
|
|
|
57
|
-
# define our
|
|
58
|
+
# define our datasource to bind the model to data
|
|
59
|
+
# testing using query fixtures is a common pattern
|
|
58
60
|
datasource word_one(
|
|
59
61
|
sentence: sentence_id,
|
|
60
62
|
word:word_one
|
|
@@ -88,25 +90,20 @@ union all
|
|
|
88
90
|
select 2 as sentence, '!'
|
|
89
91
|
''';
|
|
90
92
|
|
|
93
|
+
def concat_with_space(x,y) -> x || ' ' || y;
|
|
94
|
+
|
|
91
95
|
# an actual select statement
|
|
92
96
|
# joins are automatically resolved between the 3 sources
|
|
93
97
|
with sentences as
|
|
94
|
-
select sentence_id, word_one
|
|
98
|
+
select sentence_id, @concat_with_space(word_one, word_two) || word_three as text;
|
|
95
99
|
|
|
96
|
-
SELECT
|
|
97
|
-
--sentences.sentence_id,
|
|
98
|
-
sentences.text
|
|
99
100
|
WHERE
|
|
100
|
-
sentences.sentence_id
|
|
101
|
-
;
|
|
102
|
-
|
|
101
|
+
sentences.sentence_id in (1,2)
|
|
103
102
|
SELECT
|
|
104
|
-
--sentences.sentence_id,
|
|
105
103
|
sentences.text
|
|
106
|
-
WHERE
|
|
107
|
-
sentences.sentence_id = 2
|
|
108
104
|
;
|
|
109
|
-
|
|
105
|
+
|
|
106
|
+
|
|
110
107
|
|
|
111
108
|
```
|
|
112
109
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.3.
|
|
3
|
+
Version: 0.0.3.51
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -53,7 +53,7 @@ Installation: `pip install pytrilogy`
|
|
|
53
53
|
|
|
54
54
|
You can read more about the project [here](https://trilogydata.dev/) and try out an interactive demo [here](https://trilogydata.dev/demo/).
|
|
55
55
|
|
|
56
|
-
Trilogy:
|
|
56
|
+
Trilogy looks like SQL:
|
|
57
57
|
```sql
|
|
58
58
|
WHERE
|
|
59
59
|
name like '%lvis%'
|
|
@@ -65,7 +65,7 @@ ORDER BY
|
|
|
65
65
|
LIMIT 10;
|
|
66
66
|
```
|
|
67
67
|
## Goals
|
|
68
|
-
|
|
68
|
+
And aims to:
|
|
69
69
|
|
|
70
70
|
Preserve:
|
|
71
71
|
- Correctness
|
|
@@ -85,6 +85,7 @@ Maintain:
|
|
|
85
85
|
Save the following code in a file named `hello.preql`
|
|
86
86
|
|
|
87
87
|
```python
|
|
88
|
+
# semantic model is abstract from data
|
|
88
89
|
key sentence_id int;
|
|
89
90
|
property sentence_id.word_one string; # comments after a definition
|
|
90
91
|
property sentence_id.word_two string; # are syntactic sugar for adding
|
|
@@ -92,7 +93,8 @@ property sentence_id.word_three string; # a description to it
|
|
|
92
93
|
|
|
93
94
|
# comments in other places are just comments
|
|
94
95
|
|
|
95
|
-
# define our
|
|
96
|
+
# define our datasource to bind the model to data
|
|
97
|
+
# testing using query fixtures is a common pattern
|
|
96
98
|
datasource word_one(
|
|
97
99
|
sentence: sentence_id,
|
|
98
100
|
word:word_one
|
|
@@ -126,25 +128,20 @@ union all
|
|
|
126
128
|
select 2 as sentence, '!'
|
|
127
129
|
''';
|
|
128
130
|
|
|
131
|
+
def concat_with_space(x,y) -> x || ' ' || y;
|
|
132
|
+
|
|
129
133
|
# an actual select statement
|
|
130
134
|
# joins are automatically resolved between the 3 sources
|
|
131
135
|
with sentences as
|
|
132
|
-
select sentence_id, word_one
|
|
136
|
+
select sentence_id, @concat_with_space(word_one, word_two) || word_three as text;
|
|
133
137
|
|
|
134
|
-
SELECT
|
|
135
|
-
--sentences.sentence_id,
|
|
136
|
-
sentences.text
|
|
137
138
|
WHERE
|
|
138
|
-
sentences.sentence_id
|
|
139
|
-
;
|
|
140
|
-
|
|
139
|
+
sentences.sentence_id in (1,2)
|
|
141
140
|
SELECT
|
|
142
|
-
--sentences.sentence_id,
|
|
143
141
|
sentences.text
|
|
144
|
-
WHERE
|
|
145
|
-
sentences.sentence_id = 2
|
|
146
142
|
;
|
|
147
|
-
|
|
143
|
+
|
|
144
|
+
|
|
148
145
|
|
|
149
146
|
```
|
|
150
147
|
|
|
@@ -66,7 +66,6 @@ trilogy/core/models/environment.py
|
|
|
66
66
|
trilogy/core/models/execute.py
|
|
67
67
|
trilogy/core/optimizations/__init__.py
|
|
68
68
|
trilogy/core/optimizations/base_optimization.py
|
|
69
|
-
trilogy/core/optimizations/inline_constant.py
|
|
70
69
|
trilogy/core/optimizations/inline_datasource.py
|
|
71
70
|
trilogy/core/optimizations/predicate_pushdown.py
|
|
72
71
|
trilogy/core/processing/__init__.py
|
|
@@ -214,6 +214,8 @@ def test_math_functions(test_environment):
|
|
|
214
214
|
property order_id.order_nested <- revenue * 2/2;
|
|
215
215
|
property order_id.rounded <- round(revenue + 2.01,2);
|
|
216
216
|
property order_id.rounded_default <- round(revenue + 2.01);
|
|
217
|
+
property order_id.floor <- floor(revenue + 2.01);
|
|
218
|
+
property order_id.ceil <- ceil(revenue + 2.01);
|
|
217
219
|
constant random <- random(1);
|
|
218
220
|
select
|
|
219
221
|
order_id,
|
|
@@ -224,6 +226,8 @@ def test_math_functions(test_environment):
|
|
|
224
226
|
order_add,
|
|
225
227
|
rounded,
|
|
226
228
|
rounded_default,
|
|
229
|
+
floor,
|
|
230
|
+
ceil,
|
|
227
231
|
random,
|
|
228
232
|
;"""
|
|
229
233
|
env, parsed = parse(declarations, environment=test_environment)
|
|
@@ -2,7 +2,12 @@ from pytest import raises
|
|
|
2
2
|
|
|
3
3
|
from trilogy.core.exceptions import UndefinedConceptException
|
|
4
4
|
from trilogy.core.models.environment import Environment
|
|
5
|
-
from trilogy.parsing.parse_engine import
|
|
5
|
+
from trilogy.parsing.parse_engine import (
|
|
6
|
+
PARSER,
|
|
7
|
+
InvalidSyntaxException,
|
|
8
|
+
ParseToObjects,
|
|
9
|
+
unpack_visit_error,
|
|
10
|
+
)
|
|
6
11
|
|
|
7
12
|
TEXT = """
|
|
8
13
|
const a <- 1;
|
|
@@ -29,3 +34,20 @@ def test_parser():
|
|
|
29
34
|
with raises(UndefinedConceptException):
|
|
30
35
|
unpack_visit_error(e)
|
|
31
36
|
assert failed
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
TEXT2 = """
|
|
40
|
+
const a <- 1;
|
|
41
|
+
|
|
42
|
+
select
|
|
43
|
+
a,
|
|
44
|
+
FROM a
|
|
45
|
+
;
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_from_error():
|
|
50
|
+
env = Environment()
|
|
51
|
+
|
|
52
|
+
with raises(InvalidSyntaxException):
|
|
53
|
+
env.parse(TEXT2)
|
|
@@ -131,6 +131,7 @@ class FunctionType(Enum):
|
|
|
131
131
|
CONSTANT = "constant"
|
|
132
132
|
COALESCE = "coalesce"
|
|
133
133
|
IS_NULL = "isnull"
|
|
134
|
+
NULLIF = "nullif"
|
|
134
135
|
BOOL = "bool"
|
|
135
136
|
|
|
136
137
|
# COMPLEX
|
|
@@ -156,6 +157,8 @@ class FunctionType(Enum):
|
|
|
156
157
|
ABS = "abs"
|
|
157
158
|
SQRT = "sqrt"
|
|
158
159
|
RANDOM = "random"
|
|
160
|
+
FLOOR = "floor"
|
|
161
|
+
CEIL = "ceil"
|
|
159
162
|
|
|
160
163
|
# Aggregates
|
|
161
164
|
## group is not a real aggregate - it just means group by this + some other set of fields
|
|
@@ -279,6 +279,12 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
279
279
|
output_purpose=Purpose.PROPERTY,
|
|
280
280
|
arg_count=1,
|
|
281
281
|
),
|
|
282
|
+
FunctionType.NULLIF: FunctionConfig(
|
|
283
|
+
valid_inputs={*DataType},
|
|
284
|
+
output_purpose=Purpose.PROPERTY,
|
|
285
|
+
output_type_function=lambda args: get_output_type_at_index(args, 0),
|
|
286
|
+
arg_count=2,
|
|
287
|
+
),
|
|
282
288
|
FunctionType.COALESCE: FunctionConfig(
|
|
283
289
|
valid_inputs={*DataType},
|
|
284
290
|
output_purpose=Purpose.PROPERTY,
|
|
@@ -637,6 +643,22 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
637
643
|
output_type_function=lambda args: get_output_type_at_index(args, 0),
|
|
638
644
|
arg_count=2,
|
|
639
645
|
),
|
|
646
|
+
FunctionType.FLOOR: FunctionConfig(
|
|
647
|
+
valid_inputs=[
|
|
648
|
+
{DataType.INTEGER, DataType.FLOAT, DataType.NUMBER, DataType.NUMERIC},
|
|
649
|
+
],
|
|
650
|
+
output_purpose=Purpose.PROPERTY,
|
|
651
|
+
output_type=DataType.INTEGER,
|
|
652
|
+
arg_count=1,
|
|
653
|
+
),
|
|
654
|
+
FunctionType.CEIL: FunctionConfig(
|
|
655
|
+
valid_inputs=[
|
|
656
|
+
{DataType.INTEGER, DataType.FLOAT, DataType.NUMBER, DataType.NUMERIC},
|
|
657
|
+
],
|
|
658
|
+
output_purpose=Purpose.PROPERTY,
|
|
659
|
+
output_type=DataType.INTEGER,
|
|
660
|
+
arg_count=1,
|
|
661
|
+
),
|
|
640
662
|
FunctionType.CUSTOM: FunctionConfig(
|
|
641
663
|
output_purpose=Purpose.PROPERTY,
|
|
642
664
|
arg_count=InfiniteFunctionArgs,
|
|
@@ -1945,11 +1945,15 @@ class FilterItem(DataTyped, Namespaced, ConceptArgs, BaseModel):
|
|
|
1945
1945
|
|
|
1946
1946
|
@property
|
|
1947
1947
|
def output_datatype(self):
|
|
1948
|
-
return self.content
|
|
1948
|
+
return arg_to_datatype(self.content)
|
|
1949
1949
|
|
|
1950
1950
|
@property
|
|
1951
1951
|
def concept_arguments(self):
|
|
1952
|
-
|
|
1952
|
+
if isinstance(self.content, ConceptRef):
|
|
1953
|
+
return [self.content] + self.where.concept_arguments
|
|
1954
|
+
elif isinstance(self.content, ConceptArgs):
|
|
1955
|
+
return self.content.concept_arguments + self.where.concept_arguments
|
|
1956
|
+
return self.where.concept_arguments
|
|
1953
1957
|
|
|
1954
1958
|
|
|
1955
1959
|
class RowsetLineage(Namespaced, Mergeable, BaseModel):
|
|
@@ -247,6 +247,17 @@ def get_concept_arguments(expr) -> List["BuildConcept"]:
|
|
|
247
247
|
return output
|
|
248
248
|
|
|
249
249
|
|
|
250
|
+
class BuildParamaterizedConceptReference(BaseModel):
|
|
251
|
+
concept: BuildConcept
|
|
252
|
+
|
|
253
|
+
def __str__(self):
|
|
254
|
+
return f":{self.concept.address}"
|
|
255
|
+
|
|
256
|
+
@property
|
|
257
|
+
def safe_address(self) -> str:
|
|
258
|
+
return self.concept.safe_address
|
|
259
|
+
|
|
260
|
+
|
|
250
261
|
class BuildGrain(BaseModel):
|
|
251
262
|
components: set[str] = Field(default_factory=set)
|
|
252
263
|
where_clause: Optional[BuildWhereClause] = None
|
|
@@ -1162,7 +1173,7 @@ class BuildAggregateWrapper(BuildConceptArgs, DataTyped, BaseModel):
|
|
|
1162
1173
|
|
|
1163
1174
|
|
|
1164
1175
|
class BuildFilterItem(BuildConceptArgs, BaseModel):
|
|
1165
|
-
content:
|
|
1176
|
+
content: "BuildExpr"
|
|
1166
1177
|
where: BuildWhereClause
|
|
1167
1178
|
|
|
1168
1179
|
def __str__(self):
|
|
@@ -1170,15 +1181,27 @@ class BuildFilterItem(BuildConceptArgs, BaseModel):
|
|
|
1170
1181
|
|
|
1171
1182
|
@property
|
|
1172
1183
|
def output_datatype(self):
|
|
1173
|
-
return self.content
|
|
1184
|
+
return arg_to_datatype(self.content)
|
|
1174
1185
|
|
|
1175
1186
|
@property
|
|
1176
1187
|
def output_purpose(self):
|
|
1177
1188
|
return self.content.purpose
|
|
1178
1189
|
|
|
1190
|
+
@property
|
|
1191
|
+
def content_concept_arguments(self):
|
|
1192
|
+
if isinstance(self.content, BuildConcept):
|
|
1193
|
+
return [self.content]
|
|
1194
|
+
elif isinstance(self.content, BuildConceptArgs):
|
|
1195
|
+
return self.content.concept_arguments
|
|
1196
|
+
return []
|
|
1197
|
+
|
|
1179
1198
|
@property
|
|
1180
1199
|
def concept_arguments(self):
|
|
1181
|
-
|
|
1200
|
+
if isinstance(self.content, BuildConcept):
|
|
1201
|
+
return [self.content] + self.where.concept_arguments
|
|
1202
|
+
elif isinstance(self.content, BuildConceptArgs):
|
|
1203
|
+
return self.content.concept_arguments + self.where.concept_arguments
|
|
1204
|
+
return self.where.concept_arguments
|
|
1182
1205
|
|
|
1183
1206
|
|
|
1184
1207
|
class BuildRowsetLineage(BuildConceptArgs, BaseModel):
|
|
@@ -1578,7 +1601,7 @@ class Factory:
|
|
|
1578
1601
|
|
|
1579
1602
|
new = BuildFunction.model_construct(
|
|
1580
1603
|
operator=base.operator,
|
|
1581
|
-
arguments=[self.build(c) for c in raw_args],
|
|
1604
|
+
arguments=[self.handle_constant(self.build(c)) for c in raw_args],
|
|
1582
1605
|
output_datatype=base.output_datatype,
|
|
1583
1606
|
output_purpose=base.output_purpose,
|
|
1584
1607
|
valid_inputs=base.valid_inputs,
|
|
@@ -1615,6 +1638,8 @@ class Factory:
|
|
|
1615
1638
|
|
|
1616
1639
|
@build.register
|
|
1617
1640
|
def _(self, base: Concept) -> BuildConcept:
|
|
1641
|
+
|
|
1642
|
+
# TODO: if we are using parameters, wrap it in a new model and use that in rendering
|
|
1618
1643
|
if base.address in self.local_concepts:
|
|
1619
1644
|
return self.local_concepts[base.address]
|
|
1620
1645
|
new_lineage, final_grain, _ = base.get_select_grain_and_keys(
|
|
@@ -1738,8 +1763,8 @@ class Factory:
|
|
|
1738
1763
|
@build.register
|
|
1739
1764
|
def _(self, base: Conditional) -> BuildConditional:
|
|
1740
1765
|
return BuildConditional.model_construct(
|
|
1741
|
-
left=(self.build(base.left)),
|
|
1742
|
-
right=(self.build(base.right)),
|
|
1766
|
+
left=self.handle_constant(self.build(base.left)),
|
|
1767
|
+
right=self.handle_constant(self.build(base.right)),
|
|
1743
1768
|
operator=base.operator,
|
|
1744
1769
|
)
|
|
1745
1770
|
|
|
@@ -1767,8 +1792,8 @@ class Factory:
|
|
|
1767
1792
|
right_c, _ = self.instantiate_concept(right)
|
|
1768
1793
|
right = right_c # type: ignore
|
|
1769
1794
|
return BuildComparison.model_construct(
|
|
1770
|
-
left=(self.build(left)),
|
|
1771
|
-
right=(self.build(right)),
|
|
1795
|
+
left=self.handle_constant(self.build(left)),
|
|
1796
|
+
right=self.handle_constant(self.build(right)),
|
|
1772
1797
|
operator=base.operator,
|
|
1773
1798
|
)
|
|
1774
1799
|
|
|
@@ -2015,3 +2040,12 @@ class Factory:
|
|
|
2015
2040
|
factory.build(base.non_partial_for) if base.non_partial_for else None
|
|
2016
2041
|
),
|
|
2017
2042
|
)
|
|
2043
|
+
|
|
2044
|
+
def handle_constant(self, base):
|
|
2045
|
+
if (
|
|
2046
|
+
isinstance(base, BuildConcept)
|
|
2047
|
+
and isinstance(base.lineage, BuildFunction)
|
|
2048
|
+
and base.lineage.operator == FunctionType.CONSTANT
|
|
2049
|
+
):
|
|
2050
|
+
return BuildParamaterizedConceptReference(concept=base)
|
|
2051
|
+
return base
|
|
@@ -24,6 +24,7 @@ from trilogy.core.models.build import (
|
|
|
24
24
|
BuildFunction,
|
|
25
25
|
BuildGrain,
|
|
26
26
|
BuildOrderBy,
|
|
27
|
+
BuildParamaterizedConceptReference,
|
|
27
28
|
BuildParenthetical,
|
|
28
29
|
BuildRowsetItem,
|
|
29
30
|
LooseBuildConceptList,
|
|
@@ -447,7 +448,7 @@ class CTEConceptPair(ConceptPair):
|
|
|
447
448
|
|
|
448
449
|
|
|
449
450
|
class InstantiatedUnnestJoin(BaseModel):
|
|
450
|
-
|
|
451
|
+
object_to_unnest: BuildConcept | BuildParamaterizedConceptReference | BuildFunction
|
|
451
452
|
alias: str = "unnest"
|
|
452
453
|
|
|
453
454
|
|
|
@@ -694,7 +695,7 @@ class QueryDatasource(BaseModel):
|
|
|
694
695
|
"can only merge two datasources if the force_group flag is the same"
|
|
695
696
|
)
|
|
696
697
|
logger.debug(
|
|
697
|
-
f"
|
|
698
|
+
f"[Query Datasource] merging {self.name} with"
|
|
698
699
|
f" {[c.address for c in self.output_concepts]} concepts and"
|
|
699
700
|
f" {other.name} with {[c.address for c in other.output_concepts]} concepts"
|
|
700
701
|
)
|
|
@@ -758,7 +759,9 @@ class QueryDatasource(BaseModel):
|
|
|
758
759
|
hidden_concepts=hidden,
|
|
759
760
|
ordering=self.ordering,
|
|
760
761
|
)
|
|
761
|
-
|
|
762
|
+
logger.debug(
|
|
763
|
+
f"[Query Datasource] merged with {[c.address for c in qds.output_concepts]} concepts"
|
|
764
|
+
)
|
|
762
765
|
return qds
|
|
763
766
|
|
|
764
767
|
@property
|
|
@@ -5,7 +5,6 @@ from trilogy.core.models.build import (
|
|
|
5
5
|
)
|
|
6
6
|
from trilogy.core.models.execute import CTE, UnionCTE
|
|
7
7
|
from trilogy.core.optimizations import (
|
|
8
|
-
InlineConstant,
|
|
9
8
|
InlineDatasource,
|
|
10
9
|
OptimizationRule,
|
|
11
10
|
PredicatePushdown,
|
|
@@ -206,8 +205,6 @@ def optimize_ctes(
|
|
|
206
205
|
REGISTERED_RULES.append(PredicatePushdown())
|
|
207
206
|
if CONFIG.optimizations.predicate_pushdown:
|
|
208
207
|
REGISTERED_RULES.append(PredicatePushdownRemove())
|
|
209
|
-
if CONFIG.optimizations.constant_inlining:
|
|
210
|
-
REGISTERED_RULES.append(InlineConstant())
|
|
211
208
|
for rule in REGISTERED_RULES:
|
|
212
209
|
loops = 0
|
|
213
210
|
complete = False
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
from .base_optimization import OptimizationRule
|
|
2
|
-
from .inline_constant import InlineConstant
|
|
3
2
|
from .inline_datasource import InlineDatasource
|
|
4
3
|
from .predicate_pushdown import PredicatePushdown, PredicatePushdownRemove
|
|
5
4
|
|
|
6
5
|
__all__ = [
|
|
7
6
|
"OptimizationRule",
|
|
8
|
-
"InlineConstant",
|
|
9
7
|
"InlineDatasource",
|
|
10
8
|
"PredicatePushdown",
|
|
11
9
|
"PredicatePushdownRemove",
|
|
@@ -319,7 +319,7 @@ def generate_node(
|
|
|
319
319
|
]
|
|
320
320
|
|
|
321
321
|
logger.info(
|
|
322
|
-
f"{depth_to_prefix(depth)}{LOGGER_PREFIX} for {concept.address}, generating aggregate node with {[x
|
|
322
|
+
f"{depth_to_prefix(depth)}{LOGGER_PREFIX} for {concept.address}, generating aggregate node with {[x for x in agg_optional]}"
|
|
323
323
|
)
|
|
324
324
|
return gen_group_node(
|
|
325
325
|
concept,
|
|
@@ -67,14 +67,14 @@ def resolve_condition_parent_concepts(
|
|
|
67
67
|
def resolve_filter_parent_concepts(
|
|
68
68
|
concept: BuildConcept,
|
|
69
69
|
environment: BuildEnvironment,
|
|
70
|
-
) -> Tuple[
|
|
70
|
+
) -> Tuple[List[BuildConcept], List[Tuple[BuildConcept, ...]]]:
|
|
71
71
|
if not isinstance(concept.lineage, (BuildFilterItem,)):
|
|
72
72
|
raise ValueError(
|
|
73
73
|
f"Concept {concept} lineage is not filter item, is {type(concept.lineage)}"
|
|
74
74
|
)
|
|
75
75
|
direct_parent = concept.lineage.content
|
|
76
76
|
base_existence = []
|
|
77
|
-
base_rows = [direct_parent]
|
|
77
|
+
base_rows = [direct_parent] if isinstance(direct_parent, BuildConcept) else []
|
|
78
78
|
condition_rows, condition_existence = resolve_condition_parent_concepts(
|
|
79
79
|
concept.lineage.where
|
|
80
80
|
)
|
|
@@ -90,11 +90,10 @@ def resolve_filter_parent_concepts(
|
|
|
90
90
|
|
|
91
91
|
if concept.lineage.where.existence_arguments:
|
|
92
92
|
return (
|
|
93
|
-
concept.lineage.content,
|
|
94
93
|
unique(base_rows, "address"),
|
|
95
94
|
base_existence,
|
|
96
95
|
)
|
|
97
|
-
return
|
|
96
|
+
return unique(base_rows, "address"), []
|
|
98
97
|
|
|
99
98
|
|
|
100
99
|
def gen_property_enrichment_node(
|